发布于 2026-01-05 1 阅读
0

掌握 JavaScript Promise 的五个专业技巧

掌握 JavaScript Promise 的五个专业技巧

事件处理,尤其是 Promise,绝对是 JavaScript 最棒的特性。你可能已经熟悉这个概念了,简单来说,PromiseJavaScript 中的Promise 就是一个会返回结果的回调函数

因此,一个 Promise 可以由两个函数构成:一个在成功时调用,另一个在出错时调用。以下是一个会在一秒后随机失败或被拒绝的 Promise:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    const randomBool = Math.random() > 0.5;
    console.log(randomBool);
    // Return any value, or no value at all
    if (randomBool) resolve("I am resolved!");
    // Reject with an error, some value or nothing at all
    else reject("On no!");
  }, 1_000);
});
Enter fullscreen mode Exit fullscreen mode

在浏览器控制台或 Node REPL 中尝试运行此命令(node不带任何参数)。一秒钟后,您应该会在控制台中看到truefalse看到一条日志。如果 Promise 失败,您会看到一条错误消息(或一条警告,提示 Promise 未在 Node 中捕获)。现在我们有了可以尝试的东西,接下来就是我承诺的技巧(一语双关):

秘诀一:承诺从现在开始

正如你在示例中看到的,即使没有使用 `resolve` 或 `reject` 链式调用,Promise 也会被解析或拒绝.then.catch一旦await你创建了 Promise,它就会立即开始执行它被告知要执行的操作。

提示二:一旦完成,承诺将反复产生相同的结果

尝试promise.then(console.log)在之前示例中定义 Promise 的同一个控制台或 REPL 中运行。它会一遍又一遍地记录完全相同的结果,没有任何延迟。尝试记录日志console.log(promise),你看到了什么?我猜是以下两种情况之一:

Promise {<rejected>: "On no!"}
Enter fullscreen mode Exit fullscreen mode

或者,如果问题已解决:

Promise { "I am resolved!" }
Enter fullscreen mode Exit fullscreen mode

你可能已经猜到了,Promise 可以处于三种状态之一:已解析pending、已赋值rejectedfulfilled已释放。关键在于,它会一直保持最终状态,直到垃圾回收器将其彻底清除🪦。

技巧三:Promise.prototype.then 接受两个回调函数

then你可以通过链式调用来获得 Promise 结果catch

promise.then(console.log).catch(console.error)
Enter fullscreen mode Exit fullscreen mode

或者,简单来说:

promise.then(console.log,console.error)
Enter fullscreen mode Exit fullscreen mode

技巧 4:Promise.prototype.then 和 Promise.prototype.catch 都会返回一个新的 Promise。

即使Promise 已被解析console.log(promise.then(()=>{},()=>{})),你仍然会收到错误Promise { <pending> }。但这并不意味着异步操作本身会被重试,而只是这些方法总是会创建一个新的 Promise,即使你的回调函数是同步的。

promise === promise.then(()=>{},()=>{})
// false
promise === promise.then(()=>promise,()=>promise)
// false
Enter fullscreen mode Exit fullscreen mode

技巧五:在适当的时候使用 Promise.all、Promise.race 和 async/await

在 ES5 引入async-await语法之前,我们都生活在回调地狱中:

promise.then(() => {
  promise.then(() => {
    promise.then(() => {
      promise.then(() => {
        console.warn("Callback hell in action");
      });
    });
  });
});
Enter fullscreen mode Exit fullscreen mode

但需要记住的是,async/await只是对这种结构的语法糖。其本质仍然是同一个链式调用,这意味着下一个 Promise只有在前一个 Promise 完成之后才会创建:

const createTimeoutPromise = (n, timeout) =>
  new Promise((resolve) =>
    setTimeout(() => {
      console.log(`Promise #${n} is fulfilled`);
      resolve(n);
    }, timeout)
  );

(async () => {
  const now = Date.now();
  await createTimeoutPromise(1, 1_000);
  await createTimeoutPromise(2, 1_000);
  await createTimeoutPromise(3, 1_000);
  console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();

// Promise #1 is fulfilled
// Promise #2 is fulfilled
// Promise #3 is fulfilled
// Operation took 3.0 s
Enter fullscreen mode Exit fullscreen mode

因此,如果您只想完成所有任务,无论顺序如何,请使用以下方法Promise.all加快速度:

(async () => {
  const now = Date.now();
  const results = await Promise.all([
    createTimeoutPromise(1,1_000),
    createTimeoutPromise(2,999),
    createTimeoutPromise(3,998),
  ]);
  console.log(results)
  console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();

// Promise #3 is fulfilled
// Promise #2 is fulfilled
// Promise #1 is fulfilled
// [ 1, 2, 3 ]
// Operation took 1.0 s
Enter fullscreen mode Exit fullscreen mode

如您所见,尽管承诺的履行顺序并非如此,但您仍将按照您指定的顺序获得承诺的结果。

在极少数情况下,你可能不需要履行所有承诺,只需履行其中任何一个Promise.race即可。为了得到父亲的恩宠,就让他们履行吧👑:

(async () => {
  const now = Date.now();
  const results = await Promise.race([
    createTimeoutPromise(1,1_000),
    createTimeoutPromise(2,999),
    createTimeoutPromise(3,998),
  ]);
  console.log(results)
  console.log(`Operation took`, ((Date.now() - now) / 1_000).toFixed(1), "s");
})();

// Promise #3 is fulfilled
// 3
// Operation took 1.0 s
// Promise #2 is fulfilled
// Promise #1 is fulfilled
Enter fullscreen mode Exit fullscreen mode

请记住,如果其中任何一项承诺未能兑现,双方Promise.allPromise.race将拒绝。

今天就到这里啦,但我保证以后还会有更多(明白我的意思吗?)。

您还有其他妙招吗?欢迎在评论区分享!


照片由Andrew Petrov拍摄,来自Unsplash

文章来源:https://dev.to/valeriavg/ Five-pro-tips-to-master-promises-in-js-c2h