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

时间:2017-09-23 03:23:34

标签: javascript promise theory event-loop

我很好奇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

The visualized effect显示promise.then的回调没有进入事件循环的任务队列。这是对的吗?

【注意:问题与Promise vs setTimeout不一样,因为它更关注事件循环和承诺之间的关系】

2 个答案:

答案 0 :(得分:12)

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

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

有两种微任务:

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

macrotask队列主要包含Nodejs中的setTimeoutsetIntervalsetImmediaterequestAnimationFrameI/O

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

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

    1. 首先,您的承诺初始化new Promiseresolve是同步的;
    2. 然后将setTimeout macroTask同步添加到macrotask队列中;
    3. 然后同步将微任务promise.then(function(){})添加到微任务队列,此任务将立即运行,因为Promise初始化和解析是同步的,此任务在任何macrotask之前运行;所以,console.log p1 fulfilled;
    4. 然后将第二个macrotask setTimeout添加到macrotask队列;
    5. 此事件循环结束后,运行两个macrotasks;
    6. 代码:

      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. 首先将三个macrotask setTimeout添加到macrotask队列,将微任务promise.then()添加到微任务队列;
      2. 运行macrotask;
      3. 如果条件为true,则运行所有微任务,但它是假的,所以转到下一步;
      4. 运行第二个macrotask;
      5. 检查承诺是否已解决,条件为真,然后运行所有微任务;
      6. 继续运行其他macrotasks;

答案 1 :(得分:0)

根据Axel Rauschmayer博士here,除非堆栈没有应用程序代码,否则不会调用Promises。

  

...... Promises / A +规范要求   始终使用后一种执行模式。它通过这样说明   对于then()方法遵循requirement(2.2.4):

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

重要的是要注意:

  

这意味着你的代码可以依赖于run-to-completion语义(如   在第1部分中解释,并且链接承诺不会使其他人挨饿   处理时间的任务。