首页 > 解决方案 > 链式承诺如何在微任务队列中排队

问题描述

(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)

})()

这给出了与第一个片段相同的结果,值为a1 和b2

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

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

标签: javascriptpromiseevent-loop

解决方案


运行节点 12.3.1,我可以在更改setTimeout(res(123))setTimeout(() => res(123)).

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


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

  1. 由于 promise 被解析,.then所以将回调添加() => a = 1到队列中。
  2. () => b = 2被添加到队列中。
  3. await 1 () => console.log(a); console.log(b)2之后的代码被添加到队列中。
  4. 运行第 1 步中的回调,a设置为 1
  5. b设置为 2
  6. ab记录下来。

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


在第二个片段中:

  1. 回调() => a = 1被添加到队列中.then
  2. 第一个.then返回一个未解决的新承诺,因为第一个回调尚未运行。然后 second.then附加() => b = 2到未决的承诺。
  3. 等待之后的代码() => console.log(a); console.log(b)被添加到队列中。
  4. 回调() => a = 1运行,并履行在步骤 2 中创建的承诺。这会导致() => b = 2被添加到队列中。
  5. ab记录下来。
  6. 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”在节点 12 中提前两步出现的原因。


  1. 您可以拥有await将其余代码转换为回调的简化心智模型。
  2. 事实上,“其余代码”也解决了异步函数创建的承诺。
  3. 呵呵,so.thenawait毕竟是不同的。

推荐阅读