当前位置: 代码迷 >> 综合 >> js执行顺序之Even Loop
  详细解决方案

js执行顺序之Even Loop

热度:40   发布时间:2023-09-05 19:24:28.0

event loop都不陌生,是指主线程从“任务队列”中循环读取任务,比如


 
  1. 例1:

  2.  
  3. setTimeout(function(){console.log(1)},0);

  4.  
  5. console.log(2)

  6.  
  7. //输出2,1

在上述的例子中,我们明白首先执行主线程中的同步任务,当主线程任务执行完毕后,再从event loop中读取任务,因此先输出2,再输出1。

event loop读取任务的先后顺序,取决于任务队列(Job queue)中对于不同任务读取规则的限定。比如下面一个例子:


 
  1. 例2:

  2.  
  3. setTimeout(function () {

  4. console.log(3);

  5. }, 0);

  6.  
  7. Promise.resolve().then(function () {

  8. console.log(2);

  9. });

  10. console.log(1);

  11. //输出为 1 2 3

先输出1,没有问题,因为是同步任务在主线程中优先执行,这里的问题是setTimeout和Promise.then任务的执行优先级是如何定义的。

执行顺序

在Job queue中的队列分为两种类型:macro-task和microTask。我们举例来看执行顺序的规定,我们设


 
  1. macro-task队列包含任务: a1, a2 , a3

  2. micro-task队列包含任务: b1, b2 , b3

执行顺序为,首先执行marco-task队列开头的任务,也就是 a1 (a1代表同步的主任务)任务,执行完毕后,在执行micro-task队列里的所有任务,也就是依次执行b1, b2 , b3(异步),执行完后清空micro-task中的任务,接着执行marco-task中的第二个任务(异步),依次循环。

了解完了macro-task和micro-task两种队列的执行顺序之后,我们接着来看,真实场景下这两种类型的队列里真正包含的任务(我们以node V8引擎为例),在node V8中,这两种类型的真实任务顺序如下所示:

macro-task队列真实包含任务:

script(主程序代码)[对应上方的a1],setTimeout, setInterval, setImmediate, I/O, UI rendering

micro-task队列真实包含任务:

process.nextTick, Promises, Object.observe, MutationObserver

由此我们得到的执行顺序应该为:

script(主程序代码)—>process.nextTick—>Promises...——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering

在ES6中macro-task队列又称为ScriptJobs,而micro-task又称PromiseJobs

举例

(1) setTimeout和promise


 
  1. 例3:

  2.  
  3. setTimeout(function () {

  4. console.log(3);

  5. }, 0);

  6.  
  7. Promise.resolve().then(function () {

  8. console.log(2);

  9. });

  10.  
  11. console.log(1);

(2) process.nextTick和promise、setTimeout


 
  1. 例子4:

  2. setTimeout(function(){console.log(1)},0);

  3.  
  4. new Promise(function(resolve,reject){

  5. console.log(2);

  6. resolve();

  7. }).then(function(){console.log(3)

  8. }).then(function(){console.log(4)});

  9.  
  10. process.nextTick(function(){console.log(5)});

  11.  
  12. console.log(6);

  13. //输出2,6,5,3,4,1

(3)更复杂的例子


 
  1. setTimeout(function(){console.log(1)},0);

  2.  
  3. new Promise(function(resolve,reject){

  4. console.log(2);

  5. setTimeout(function(){resolve()},0)

  6. }).then(function(){console.log(3)

  7. }).then(function(){console.log(4)});

  8.  
  9. process.nextTick(function(){console.log(5)});

  10.  
  11. console.log(6);

  12.  
  13. //输出的是 2 6 5 1 3 4

这些例子的原因请根据执行顺序执行判断,这里不一一解释了

  相关解决方案