什么是EventLoop?

  咱们知道js的最大特色就是单线程,就是同一时间只能作一件事。即使HTML5提出Web Worker容许JavaScript脚本建立多个线程,可是子线程彻底受主线程控制,且不得操做DOM。因此,这个新标准并无改变JavaScript单线程的本质。javascript

一、任务队列

  由于js的单线程的特性,全部运行在js线程中的代码须要根据某种规则来按队列执行。由于js中既有同步事件,又有异步事件,因此有时候咱们在工做中写js代码时总会一脸懵逼,特别是在浏览器环境中进行断点调试时,因此弄懂js代码的执行顺序很是重要。

   按照js回调事件的特性,将任务分为同步任务和异步任务。html

  • 一、全部同步任务都在主线程中执行,并造成一个执行栈,异步任务在有了运行结果以后才会将回调函数添加到任务队列中。
  • 二、代码运行时会先去执行主线程中的代码,等到主线程中的代码执行完毕后,才会去读取任务队列中的回调函数并放置到执行栈上来执行,先添加到任务队列中的会被先执行。等到任务队列中的回调函数执行完毕后,又会回到主线程上来。
  • 三、主线程中会不断重复执行第二步,并造成一个循环。这就是所谓的EventLoop。

二、事件队列

  任务队列按照某种条件又能够细分为microtask 和 macrotask,一般咱们会称之为微任务和宏任务。代码执行的优先级为:主线程>微任务>宏任务。java

  • 微任务主要有:ES6的Promise(then函数),node.js中的process.nextTick(),MessageChannel(消息通道,相似worker)
  • 宏任务主要有:总体代码script、setTimeout 和 setInterval、还有 MessageChannel,setImmediate, I/O

  js中代码执行的顺序是:首先执行主线程中的代码(宏任务),开始第一次循环,执行完毕后,再执行全部的微任务,而后再执行宏任务,看该宏任务中是否有微任务,若是有的话,就将全部的微任务执行完毕。再执行新的宏任务,如此不断的反复执行下去。
事件执行机制(即js代码执行机制)以下图(图片来自ssssyoki)所示。node

详情请看以下代码

//宏任务
        setTimeout(function(){
            console.log("我是宏任务1");
        })
        let p=new Promise((resolve,reject)=>{
            console.log("promise1");
            resolve();
        });
        //微任务
        p.then(res=>{
            console.log("我是微任务1")
        });
        let p2=new Promise((resolve,reject)=>{
            console.log("promise2");
            resolve();
        });
        //微任务
        p2.then(res=>{
            console.log("我是微任务2")
        });
        //宏任务
        setTimeout(function(){
            console.log("我是宏任务2");
        })
        console.log("我是主线程");
      
复制代码

执行结果以下所示:promise

  代码解析:浏览器

  • 一、js代码开始执行后,遇到setTimeout,将其回调函数添加到宏任务队列中,记为setTimeout1;
  • 二、遇到Promise,Promise构造函数中的代码是同步任务,因此最早输出promise1,同时将then函数添加到微任务队列中,记为then1;
  • 三、接着再次遇到Promise,而后输出promise2,并将其then函数添加在微任务队列中,记为then2。
  • 四、执行到setTimeout,将其回调函数添加到宏任务队列中,记为setTimeout2。
  • 五、最后输出"我是主线程"。此时主线程中的代码执行完毕。此时任务队列中的状况以下。
宏任务 微任务
setTimeout1 then1
setTimeout2 then2
  • 六、接着开始执行微任务队列中的全部任务,输出"我是微任务1","我是微任务2",此时第一轮事件事件循环结束。
  • 七、随着setTimeout1回调事件的执行,意味着第二轮事件循环的开始,接着输出"我是宏任务1",它的结束意味着第二轮事件循环的结束。
  • 八、随着setTimeout2回调事件的执行,意味着第三轮事件循环的开始,它的结束就意味着第三轮事件循环的结束。
  • 九、开始第二次循环,执行第一个宏任务中的代码,并输出"我是宏任务1",接着输出"我是宏任务2"。 因此个人理解其实就是宏任务队列中有几个宏任务,就意味着在这次js代码执行过程当中有几回事件循环。

三、浏览器环境中和node环境中EventLoop的异同

  示例代码以下所示;
//宏任务,第二次事件循环的开始
        setTimeout(function() {
            console.log('setTimeout1');
            new Promise(function(resolve) {
                console.log('setTimeout1-Promise');
                resolve();
                //微任务
            }).then(function() {
                console.log('setTimeout1-then')
            })
        })
        new Promise(function(resolve) {
            console.log('Promise');
            resolve();
            //微任务
        }).then(function() {
            console.log('then')
        })
        //宏任务,第三次事件循环的开始
        setTimeout(function() {
            console.log('setTimeout2');
            new Promise(function(resolve) {
                console.log('setTimeout2-promise');
                resolve();
            }).then(function() {
                console.log('setTimeout2-then')
            })
        })
复制代码

一、浏览器中js事件循环机制

  上面代码在浏览器环境中的输出结果以下所示:网络

  因此我得出结论:在浏览器环境下js代码的执行顺序是。主线程>微任务队列中全部的回调函数>宏任务队列中的全部宏任务。主线程和微任务成为第一次事件循环。宏任务队列中的一个宏任务就是一个事件循环。

二、node环境中js事件循环机制

  我的以为node中的js执行机制比在浏览器中的要复杂一些。盗用掘金网友的一张图很好的解释了node环境中的事件循环机制。

  node环境中的eventLoop是按阶段来执行的,主要有6个阶段,这个阶段里的代码执行完毕,才会去执行下一个阶段里的代码。6个阶段中的代码都执行完毕才算是完成一个事件循环。

  • Node的Event Loop分阶段,阶段有前后,依次是
       一、expired timers and intervals,即到期的setTimeout/setInterval
       二、I/O events,包含文件,网络等等
       三、immediates,经过setImmediate注册的函数
       四、close handlers,close事件的回调,好比TCP链接断开
  • 同步任务及每一个阶段以后都会清空microtask队列
       一、优先清空next tick queue,即经过process.nextTick注册的函数
       二、再清空other queue,常见的如Promise
  • 而和规范的区别,在于node会清空当前所处阶段的队列,即执行全部task

  而在node环境中的输出结果是这样的,两次执行结果还不同异步

  上面的代码在node环境中的执行结果以下所示。 函数

结果分析:oop

  • 首先执行主线程中的代码,输出promise1。
  • 接着清空微队列中的任务,即执行外层then函数中的代码,输出then1(微任务不在任何一个阶段执行,在各个阶段切换的中间执行)。
  • 接着开始执行timer阶段中的全部任务,因此此时输出setTimeOut1——>setTimeOut1-Promise——>setTimeOut2——>setTimeOut2-Promise。
  • timer阶段中的任务执行完毕后,又开始执行微任务队列中的全部任务。因此此时输出setTimeOut1-then、setTimeOut1-then2。

最后: node中的eventLoop运行机制比较复杂,因此还须要花费更多的时间去多多研究。

参考文档
一、这一次,完全弄懂 JavaScript 执行机制
二、JavaScript 运行机制详解:再谈Event Loop
三、Eventloop不可怕,可怕的是赶上Promise
四、浏览器说:虽然都叫event loop,可是我和node不同

相关文章
相关标签/搜索