链式承诺如何在微任务队列中排队

how are chained promises queued in microtasks queue

(async function() {
    var a,b;

    function flush(){
        return new Promise(res => {
            res(123)
    })}

    Promise.resolve().then(() => a = 1)
    Promise.resolve().then(() => b = 2)

    await flush();

    console.log(a)
    console.log(b)

})()

在此代码段中,ab 的值记录在控制台中。

(async function() {
    var a;
    var b;

    function flush(){
        return new Promise(res => {
            res(123)
    })}

    Promise.resolve().then(() => a = 1).then(() => b = 2)   

    await flush();

    console.log(a)
    console.log(b)

})()

在这种情况下,a 的值记录为 1,而 b 未定义。

(async function() {
    var a;
    var b;

    function flush(){
        return new Promise(res => {
            setTimeout(res)
    })}

    Promise.resolve().then(() => a = 1).then(() => b = 2)   

    await flush();

    console.log(a)
    console.log(b)

})()

这给出了与第一个片段相同的结果,值 a 为 1,b 为 2

我想了解,为什么 promise 链接的行为与多个单独的 promise 不同

PS: 我对微任务队列和事件循环有了基本的了解

运行 节点 12.3.1,我可以重现问题中所述的观察结果, 将 setTimeout(res(123)) 更改为 setTimeout(() => res(123)).

在 JavaScript 中,并发模型是事件循环,其中单个线程从队列中执行回调。


在第一个片段中,发生了以下情况。

  1. 由于承诺已重新提交,.then 将回调 () => a = 1 添加到队列中。
  2. () => b = 2 已添加到队列中。
  3. await1() => console.log(a); console.log(b)2后的代码加入队列
  4. 第1步的回调是运行,a设置为1
  5. b 设置为 2
  6. ab 已记录。

由于设置变量发生在打印它们之前,因此您会同时看到 1 和 2。


在第二个片段中:

  1. 回调 () => a = 1.then
  2. 添加到队列中
  3. 第一个 .then returns 一个新的承诺,尚未解决,因为第一个回调尚未 运行。然后第二个 .then 附加 () => b = 2 到未决的承诺。
  4. 等待() => console.log(a); console.log(b)后的代码加入队列
  5. 回调 () => a = 1 是 运行,并实现了在步骤 2 中创建的承诺。这导致 () => b = 2 被添加到队列中。
  6. ab 已记录。
  7. b = 2 是 运行,但这发生在 b 之后,它是 undefined,被打印出来。

但是在 Firefox 中,所有三个片段的输出都是相同的。 我通过添加 async 设法产生了上述行为。 Promise.resolve().then(async () => a = 1).then(() => b = 2)

这是一个展示相同问题的简化版。 在 Node 中为 1 5 2 3 4 但在 Firefox 中为 1 2 3 5 4。

(async function() {
  Promise.resolve()
    .then(() => console.log(1))
    .then(() => console.log(2))
    .then(() => console.log(3))
    .then(() => console.log(4))
  await Promise.resolve()
  console.log(5)
})()

但是如果您将 await 更改为 .thenPromise.resolve().then(() => console.log(5))

你在两个平台上得到 1 5 2 3 4。3

为什么?我用谷歌搜索并找到了这个:https://v8.dev/blog/fast-async

节点 12 使用 await 优化了一些额外的步骤,这在以前需要额外的一次性承诺和两个微滴答。这似乎是“5”在 Node 12 中提前两步出现的原因。


  1. 您可以拥有简化的心智模型,await 将其余代码转换为回调。
  2. 事实上"the rest of the code"也解析了异步函数创建的promise。
  3. 呵呵,所以.thenawait毕竟不一样