JavaScript 中带有 Promise 的事件循环

问题描述 投票:0回答:2

直到今天我以为我知道 javascript 中的事件循环是如何工作的,但我遇到了一个非常奇怪的问题。也许这对你来说并不奇怪,那么如果你能向我解释一下,我将不胜感激,所以这里是代码示例:

Promise.resolve()
  .then(() => {
      Promise.resolve().then(() => {
          Promise.resolve().then(() => {
              Promise.resolve().then(() => {
                  console.log('first mega inner then')
              })

              console.log('first very inner then')
          })

          console.log('first inner then')
      })

      console.log('first then')
  })
  .then(() => {
      Promise.resolve().then(() => {
          Promise.resolve().then(() => {
              console.log('second very inner then')
          })

          console.log('second inner then')
      })

      console.log('second then')
  })

为什么console.logs的顺序是:

  1. 先然后
  2. 先内然后
  3. 第二次
  4. 首先非常内在
  5. 第二个内然后
  6. 首先是巨型内部然后
  7. 第二个非常内在的然后

我个人期望是:

  1. 先然后
  2. 先内然后
  3. 首先非常内在
  4. 首先是巨型内部然后
  5. 第二次
  6. 第二个内然后
  7. 第二个非常内在的然后

但这还不是结束...对我来说最有趣的是当我在第二个“then”之后添加queueMicrotask时

some code here...
.then(() => {
    Promise.resolve().then(() => {
        Promise.resolve().then(() => {
            console.log('second very inner then')
        })

        console.log('second inner then')
    })

    console.log('second then')
})

queueMicrotask(() => console.log('microtask'))

它在第一个“THEN”之后执行,所以现在的顺序是:

  1. 先然后
  2. 微任务
  3. 先内然后
  4. 第二次
  5. 首先非常内在
  6. 第二个内然后
  7. 首先是巨型内部然后
  8. 第二个非常内在的然后

发生什么事了?我一点都没懂。我认为这是我在 JS 中最不明白的事情,如果你能解释为什么它是这样工作的,我将非常感激你,直到我知道这一点我才能入睡😂

javascript node.js es6-promise event-loop
2个回答
0
投票

这里可能对promise的使用方式有误解。在 Promise 链中,一旦 Promise 解析,

.then
方法就会被激活。如果你想延迟链的第二部分的操作,你需要在新的 Promise 中指定何时解析。这个例子展示了它是如何工作的。

当你调用

Promise.resolve().then
时,它会调用Promise类的resolve方法,并使then方法立即可用。虽然立即调用resolve方法并没有延迟resolve,但是执行resolve仍然需要一些时间,所以下面这行代码不会被阻塞。

希望这能让您深入了解 js 中 Promise 的魔力

(new Promise(resolve => {
  Promise.resolve().then(() => {
    Promise.resolve().then(() => {
      Promise.resolve().then(() => {
        console.log('first mega inner then')
        resolve();
      })
      console.log('first very inner then')
    })
    console.log('first inner then')
  })
  console.log('first then')
}))
.then(() => {
  Promise.resolve().then(() => {
    Promise.resolve().then(() => {
      console.log('second very inner then');;
    });
    console.log('second inner then');
  });
  console.log('second then');
});


0
投票

理解 JavaScript 事件循环以及 Promise 和微任务的执行顺序确实非常复杂,但我会尽力为您澄清。

JavaScript 的事件循环处理任务和微任务。任务包括渲染、用户事件和脚本执行等内容,而微任务通常是 Promise 回调和类似

queueMicrotask
的操作。

关键是:微任务在当前任务结束时、JavaScript 引擎将控制权交还给事件循环之前进行处理。这意味着队列中的所有微任务都会在下一个任务开始之前执行。这就是为什么您会在

microtask
之后看到
first then
记录,因为
queueMicrotask
添加了一个微任务,该微任务在当前脚本(第一个任务)之后立即执行,但在事件循环继续执行下一个任务之前。

现在,让我们分解承诺链:

  1. Promise.resolve()
    创建一个立即解决的承诺 (P1)。
  2. P1 上的
  3. .then()
    安排微任务 (MT1) 来记录
    first then
  4. 在 MT1 内部,创建并解决了另一个 Promise (P2),调度另一个微任务 (MT2) 来记录
    first inner then
  5. 事件循环继续到 P1 上的下一个
    .then()
    ,调度 MT3 记录
    second then
  6. 现在,事件循环已完成当前任务并开始处理微任务(MT1、MT2、MT3)。
  7. MT1 日志
    first then
  8. MT2 日志
    first inner then
  9. MT3 记录
    second then
    并安排 MT4(记录
    second inner then
    )和 MT5(记录
    second very inner then
    )。
  10. 在处理 MT4 和 MT5 之前,
    queueMicrotask
    安排 MT6 记录
    microtask
  11. MT6 日志
    microtask
  12. 事件循环现在处理 MT4 和 MT5,记录
    second inner then
    second very inner then
  13. 同时,MT1和MT2内的嵌套promise调度MT7(记录
    first very inner then
    )和MT8(记录
    first mega inner then
    ),它们在MT5之后处理。

执行顺序由微任务的排队和处理方式决定。每个

.then()
处理程序都会对一个微任务进行排队,并且嵌套的 Promise 将进一步的微任务排队,这些微任务将在当前任务完成后按照先进先出 (FIFO) 的顺序进行处理。

所以,最终的日志顺序是:

  1. first then
    (MT1)
  2. microtask
    (MT6)
  3. first inner then
    (MT2)
  4. second then
    (MT3)
  5. first very inner then
    (MT7)
  6. second inner then
    (MT4)
  7. first mega inner then
    (MT8)
  8. second very inner then
    (MT5)

此序列反映了在事件循环移至下一个任务之前微任务队列正在处理完成,从而确保在启动任何新任务之前完成所有微任务。这是 JavaScript 并发模型的一个微妙但强大的方面,可能会导致您所经历的那种混乱。我希望这个解释有助于澄清问题! 😄

© www.soinside.com 2019 - 2024. All rights reserved.