事件循环和 Promise 之间的关系是什么

IT技术 javascript promise theory event-loop
2021-03-22 18:46:58

我很好奇 Event Loop 和 Promise 的关系。
演示暴露了这个问题。我预计p1 fulfilled会出现在中间,因为它们将一个任务排到同一个任务队列中,并一个一个地执行。

var p1 = new Promise(function(resolve, reject){
    resolve(1)
})
setTimeout(function(){
  console.log("will be executed at the top of the next Event Loop")
},0)
p1.then(function(value){
  console.log("p1 fulfilled")
})
setTimeout(function(){
  console.log("will be executed at the bottom of the next Event Loop")
},0)

控制台结果是:

p1 fulfilled
will be executed at the top of the next Event Loop
will be executed at the bottom of the next Event Loop

可视化效果显示promise.then的回调没有进入 Event Loop 的任务队列。这是正确的?

【注意:这个问题与Promise vs setTimeout 不同,因为它更关注 Event Loop 和 Promise 之间的关系】

2个回答

每个事件循环都有一个微任务队列和一个宏任务队列。

微任务是原本要在微任务队列而不是任务队列上排队的任务。请参阅https://www.w3.org/TR/html51/webappapis.html#microtask-queue

有两种微任务:

  • 单独回调微任务,例如Promise
  • 和复合微任务,例如Node.js 中的Object.observeMutationObserverprocess.nextTick

而宏任务队列主要包含setTimeout, setInterval, setImmediate, requestAnimationFrame,I/O在 Nodejs 中。

在一个事件循环中,这两个任务队列将分两步运行:

  1. 首先检查旧的宏任务队列中是否有宏任务(称为X);
  2. 如果X存在并且正在运行,则等待它进入下一步,直到它完成;否则,立即进入下一步;
  3. 其次,运行微任务队列的所有微任务;
  4. 当运行微任务时,我们仍然可以向队列中添加更多的微任务,这些任务也会运行。

在你的例子中:

  1. 首先,您的 Promise 初始化new Promise并且resolve是同步的;
  2. 然后同步添加一个setTimeoutmacroTask到macrotask队列中;
  3. 然后将微promise.then(function(){})任务同步添加到微任务队列中,这个任务会立即运行,因为 Promise 的初始化和解析是同步的,这个任务在任何宏任务之前运行;所以, console.log p1 fulfilled;
  4. 然后将第二个宏setTimeout任务添加到宏任务队列中;
  5. 在这个事件循环结束后,运行两个宏任务;

对于此代码:

setTimeout(function(){
  console.log("will be executed at the top of the next Event Loop")
},0)
var p1 = new Promise(function(resolve, reject){
    setTimeout(function(){resolve(1)},0)
});
setTimeout(function(){
    console.log("will be executed at the bottom of the next Event Loop")
},0)
for (var i = 0; i < 100; i++) {
    (function(j){
        p1.then(function(value){
           console.log("promise then - " + j)
        });
    })(i)
}

输出顺序:

will be executed at the top of the next Event Loop
promise then - 0
promise then - 1
promise then - 2
...
promise then - 99
will be executed at the bottom of the next Event Loop
  1. 首先将三个setTimeoutmacrotask添加promise.then()到macrotask queue ,一个microtask添加到microtask queue;
  2. 运行宏任务;
  3. 如果条件为真,则运行所有微任务,但为假,则进行下一步;
  4. 运行第二个宏任务;
  5. 检查 promise 是否已解决,条件为真,然后运行所有微任务;
  6. 继续运行其他宏任务;
因为你的promise initialize 和resolve 是同步的,所以在第一步的时候,检查旧的macrotask queue 中是否有macrotask(叫做X),会返回false。只有在同步代码之后,setTimeoutmacrotask 才会被调用。
2021-04-22 18:46:58
既然w3.org没说macrotask queue,那么macrotask queue和task queue有什么区别呢?从概念上讲,微任务队列是一种任务队列还是完全不同?
2021-05-09 18:46:58
“单独回调微任务,例如 Promise”在这里更具体是否正确,而不是说明:“单独回调微任务,只能使用 Promise.prototype.then() 或 await 创建”
2021-05-10 18:46:58
你说X会先检查,但是,是setTimeout第2步中macroTaskX吗?为什么不先执行呢?
2021-05-13 18:46:58

除非按照 Axel Rauschmayer 博士这里的规定,除非堆栈中没有应用程序代码,否则不会调用 Promise

... Promises/A+ 规范要求始终使用后一种执行模式。它通过then() 方法的以下要求(2.2.4) 说明了这一点:

在执行上下文堆栈仅包含平台代码之前,不得调用 onFulfilled 或 onRejected。

重要的是要注意:

这意味着您的代码可以依赖 run-to-completion 语义(如第 1 部分所述),并且链接Promise不会使其他任务的处理时间挨饿。

我不明白这是如何回答问题的。在 OP 的示例中,该then子句有待处理setTimeout任务时调用,假设那些被认为是“应用程序代码”或“平台代码”,我不知道它们的定义——它们是什么意思?
2021-05-01 18:46:58