如何预测 Promise.then 链上的微任务顺序?

Mat*_*ith 2 javascript event-loop promise es6-promise

决定此代码输出的 JavaScript 规则是什么?:

Promise.resolve()
    .then(() => console.log("Microtask 1"))
    .then(() => console.log("Microtask 11"));

Promise.resolve()
    .then(() => console.log("Microtask 2"))
    .then(() => console.log("Microtask 22"))
Run Code Online (Sandbox Code Playgroud)

这是输出:

Microtask 1
Microtask 2
Microtask 11
Microtask 22
Run Code Online (Sandbox Code Playgroud)

但为什么这不是输出?:

Microtask 1
Microtask 11
Microtask 2
Microtask 22
Run Code Online (Sandbox Code Playgroud)

T.J*_*der 5

您会看到所看到的输出,因为微任务 forMicrotask 11直到运行微任务才排队Microtask 1,此时微任务 forMicrotask 2已在队列中等待。所以顺序是(跳过一些细节;更多内容见下文):

\n
    \n
  1. 将微任务排队Microtask 1
  2. \n
  3. 将微任务排队Microtask 2
  4. \n
  5. 任务已完成,开始处理微任务:\n
      \n
    1. 执行 #1 中排队的微任务\n
        \n
      • 输出Microtask 1
      • \n
      • 将微任务排队Microtask 11
      • \n
      \n
    2. \n
    3. 执行 #2 中排队的微任务\n
        \n
      • 输出Microtask 2
      • \n
      • 将微任务排队Microtask 21
      • \n
      \n
    4. \n
    5. 执行#3.1中排队的微任务\n
        \n
      • 输出Microtask 11
      • \n
      \n
    6. \n
    7. 执行#3.2中排队的微任务\n
        \n
      • 输出Microtask 21
      • \n
      \n
    8. \n
    \n
  6. \n
\n

因此,

\n
\n微任务 1\n微任务 2\n微任务 11\n微任务 21\n
\n
\n

但是:编写依赖于断开连接的承诺链/微任务序列的执行顺序的代码是自找麻烦。:-) 你只能根据你知道已经解决的承诺来推理它,当然在正常情况下,你不知道承诺何时(或什至)会解决。相反,如果您需要特定的顺序,请连接链条以确保顺序。

\n
\n

重新跳过细节:我在主要解释中掩盖了一些内容,以使其简短明了,但为了准确性,让我们看一下:

\n
Promise.resolve()\n    .then(() => console.log("Microtask 1"))\n    .then(() => console.log("Microtask 11"));\n
Run Code Online (Sandbox Code Playgroud)\n

...就其本身而言,忽略第二个承诺链以使事情变得简单。

\n

以下是该代码的执行方式(我关于 Promise 术语的博客文章在阅读以下内容时可能会有所帮助):

\n
    \n
  1. 执行Promise.resolve(),创建一个承诺,在这种特殊情况下,该承诺将得到满足undefined
  2. \n
  3. 执行.then(() => console.log("Microtask 1")),创建一个新函数并then根据 Step\xc2\xa01 中的 Promise 调用它。这会创建并返回一个新的 Promise,在这种特殊情况下,会将一个微任务排队以调用该函数,因为 Promise 已得到履行。
  4. \n
  5. 执行.then(() => console.log("Microtask 11")),创建一个新函数并then根据 Step\xc2\xa02 中的 Promise 调用它。这会创建并返回一个新的 Promise(我们在该代码中将其丢弃),并且由于 Step\xc2\xa02 中的 Promise 仍处于待处理状态,因此将函数添加到其中作为履行处理程序。
  6. \n
  7. 运行此代码的任务结束,因此 JavaScript 引擎处理微任务队列:\n
      \n
    1. 执行队列中的第一个微任务:\n
        \n
      1. 调用函数logging Microtask 1
      2. \n
      3. 由于函数返回undefined,这不是 Promise 或其他thenable,因此引擎会履行 Step\xc2\xa02 中的 Promise undefined.(如果函数返回了 Promise 或其他 thenable,则 Promise 将被解析返回。)\n
          \n
        • 这会将微任务排队以调用在 Step\xc2\xa03 中的该 Promise 上注册的履行处理程序,该处理程序将Microtask 11在调用时记录。
        • \n
        \n
      4. \n
      \n
    2. \n
    3. 执行队列中的下一个微任务(请记住,我们忽略了本描述中的另一个承诺链,因此在这种情况下,下一个微任务是Microtask 11刚刚添加到队列中的一个日志记录,而不是一个日志记录Microtask 2:\n
        \n
      1. 调用函数logging Microtask 11
      2. \n
      3. 由于函数返回undefined,这不是 Promise 或其他 thenable,因此引擎使用undefined.\n履行 Step\xc2\xa03 中的 Promise
          \n
        • 这不会做任何事情,因为该承诺没有注册任何履行处理程序。
        • \n
        \n
      4. \n
      \n
    4. \n
    \n
  8. \n
\n