前端js中的事件循环eventloop机制详解

  

  

我们知道js是单线程执行的,那么异步的代码js是怎么处理的呢?例如下面的代码是如何进行输出的:

        console.log (1);   setTimeout(函数(){   console.log (2);   },0);   新的承诺(函数(解决){   console.log (3);   解决(Date.now ());   })(函数(){   console.log (4);   });   console.log (5);   setTimeout(函数(){   新的承诺(函数(解决){   console.log (6);   解决(Date.now ());   })(函数(){   console.log (7);   });   }, 0);      

在不运行的情况可以先猜测下最终的输出,然后展开我们要说的内容。

  


  

  

依据我们多年编写ajax的经验:js应该是按照语句先后顺序执行,在出现异步时,则发起异步请求后,接着往下执行,待异步结果返回后再接着执行。但他内部是怎样管理这些执行任务的呢?

  

在js中,任务分为宏任务(macrotask)和微任务(microtask),这两个任务分别维护一个队列,均采用先进先出的策略进行执行!同步执行的任务都在宏任务上执行。

  

宏任务主要有:脚本(整体代码),setTimeout, setInterval, I/O, UI交互事件,postMessage, MessageChannel setImmediate(节点。js环境)。

  

微任务主要有:承诺。然后,process.nextTick MutationObserver(节点。js环境)。

  

具体的操作步骤如下:

  
      <李>从宏任务的头部取出一个任务执行;李   <李>执行过程中若遇到微任务则将其添加到微任务的队列中,李   <李>宏任务执行完毕后,微任务的队列中是否存在任务,若存在,则挨个儿出去执行,直到执行完毕,李   <李> GUI渲染;李   <李>回到步骤1,直到宏任务执行完毕,李   
  

这4步构成了一个事件的循环检测机制,即我们所称的eventloop。

  

回到我们上面说的代码:

        console.log (1);   setTimeout(函数(){   console.log (2);   },0);   新的承诺(函数(解决){   console.log (3);   解决(Date.now ());   })(函数(){   console.log (4);   });   console.log (5);   setTimeout(函数(){   新的承诺(函数(解决){   console.log (6);   解决(Date.now ());   })(函数(){   console.log (7);   });   }, 0);      

执行步骤如下:

  
      <李>执行日志(1)、输出1;李   <李>遇到setTimeout,将回调的代码日志(2)添加到宏任务中等待执行;李   <李>执行console.log(3),然后将中的日志(4)添加到微任务中,李   <李>执行日志(5)、输出5;李   <李>遇到setTimeout,将回调的代码日志(6、7)添加到宏任务中,李   <李>宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,存在一个微任务日志(4)(在步骤3中添加的),执行输出4,李   <李>取出下一个宏任务日志(2)执行,输出2;李   <李>宏任务的一个任务执行完毕,查看微任务队列中是否存在任务,不存在,李   <李>取出下一个宏任务执行,执行日志(6),然后将中的日志(7)添加到微任务中,李   <李>宏任务执行完毕,存在一个微任务日志(7)(在步骤9中添加的),执行输出7;李   
  

因此,最终的输出顺序为:1、3、5、4、2、6、7,

  

我们在Promise.then实现一个稍微耗时的操作,这个步骤看起来会更加地明显:

        console.log (1);   var=Date.now开始();   setTimeout(函数(){   console.log (2);   },0);   setTimeout(函数(){   控制台。日志(4,Date.now()——开始);   },400);   Promise.resolve(),然后(函数(){   var=总和函数(a, b) {   返回数字(一)+ (b);   }   var res=[];   (var=0;i<5000000;我+ +){   var=Math.floor (math . random () * 100);   var b=Math.floor (math . random () * 200);   res.push (sum (a, b));   }   res=res.sort ();   console.log (3);   })      

Promise.then中,先生成一个500年万随机数的数组,然后对这个数组进行排序。运行这段代码可以发现:马上会输出1,稍等一会儿才会输出3,然后再输出2。不论等待多长时间输出3、2一定会在3的后面输出。这也就印证了eventloop中的第3步操作,必须等所有的微任务执行完毕后,才开始下一个宏任务。

  

同时,这段代码的输出很有意思:

        setTimeout(函数(){   控制台。日志(4,Date.now()——开始);//4 1380电脑状态的不同,输出的时间差也不一样   },400)、

前端js中的事件循环eventloop机制详解