浏览器事件循环与node事件循环

浏览器事件循环

  • 浏览器宿主环境中包含5个线程:
    1. JS引擎:负责执行执行栈的最顶部代码
    2. GUI线程:负责渲染页面
    3. 事件监听线程:负责监听各种事件
    4. 计时线程:负责计时
    5. 网络线程:负责网络通信
      (js引擎和gui线程会相互等待)

当上面的线程发生了某些事请,如果该线程发现,这件事情有处理程序,它会将该处理程序加入事件队列。当JS引擎发现,执行栈中已经没有了任何内容后,会将事件队列中的第一个函数加入到执行栈中执行。

  • 只有在执行栈中,才会被执行

事件队列在不同的宿主环境中有所差异,大部分宿主环境会将事件队列进行细分。在浏览器中,事件队列分为两种:

  • 宏任务(队列):macroTask,计时器结束的回调、事件回调、http回调等等绝大部分异步函数进入宏队列
  • 微任务(队列):MutationObserver,Promise产生的回调进入微队列

举例:

    console.log('script start')
    setTimeout(()=>{
        console.log('宏队列');
    }, 1000)
    Promise.resolve().then(()=>{
        console.log('微队列');
    })
  1. 代码执行,入栈全局上下文,入栈console.log()上下文,控制台输出'script start',console.log()上下文出栈。
  2. setTimeout()执行,通知计时线程计时,Promise.resolve执行,then回调函数加入微队列,全局上下文出栈。此时执行栈空,检查微队列,把微队列第一个函数拿到执行栈执行,输出'微队列'。此时执行栈空,等待。
  3. 1s后计时结束,setTimeout的回调函数加入到宏队列。因为执行栈此时空闲,检查微队列没东西,所以把宏队列第一个函数拿到执行栈执行,输出'宏队列'。程序运行结束。

node事件循环

event loop

node环境,有很多队列,主要有以下三个,事件循环时,会挨个检查。

  1. timers: 存放计时器的回调函数
  2. poll: 轮询队列,存放大部分回调,比如文件读取,监听用户请求。
    poll中有回调,依次执行,直到清空队列;poll没有回调,轮询等待其他队列出现回调,然后进入下一阶段
  3. check: setImmediate回调函数
  • 执行一个回调之前,必须清空nextTick和promise队列

举例:

    console.log('main');
    setTimeout(()=>{
        console.log('timers')
    }, 3000)
    setImmediate(()=>{
        console.log('check')
    })

主程序执行,打印"main",然后开始事件循环。
检查timers队列,没有回调,进入下一阶段
检查poll队列,没有回调,别的队列有回调,进入下一阶段
检查check队列,输出"check"
第二圈:
检查timers队列,没有回调,进入下一阶段
检查poll队列,没有回调,等其他队列出现回调。3s时间到,timers队列出现回调函数,进入下一阶段。
检查check队列,没有回调
第三圈:
检查timers队列,输出"timers"
检查poll队列,没有回调,并且没有任何值得等待的东西了(没有监听用户请求,没有计时器等等),进入下一阶段
检查check队列,没有回调
事件循环结束!

除了上面过程,再补充两点

  1. nextTick和promise不属于事件循环,但是执行任何回调前,必须先清空nextTick再清空promise
  2. 监听某个端口时,程序不会结束,是卡在了poll,它会一直等,所以事件循环不会结束

扩展:实际上timers不是一个队列,而是一个计时器一个计时器的查看它时间到没到(有运算,所以运行会慢),时间到了就把回调拉过来。而check可以当成一个数组,里面的回调直接运行。所以 setImmediate 运行效率比 setTimeout高 (当然开定时器的过程也会有一定的时间消耗)。

下面程序可以对比setTimeout 和 setImmediate 运行效率

    let i = 0;
    console.time()
    function test(){
        i ++;
        if(i < 1000){
            setTimeout(test, 0)
            // setImmediate(test)
        } else {
            console.timeEnd();
        }
    }
    test(); // 1583.377ms  // 6.946ms
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。