浏览器端与nodejs端EventLoop机制(宏任务与微任务)的异同

  偶然看了一篇文章讲了浏览器端与nodejs端的事件循环机制,但在检验的过程中发现了浏览器与nodejs两者之间存在不符合统一解释的差异,由此引出了一系列测试,记录一下。

  在此之前,我理解的Browser的循环机制是下面这样的:

nodejs的循环机制是:

主线程去执行同步代码、异步非I/O代码 ,异步I/O代码则有主线程分配给线程池的子线程执行,执行完毕后将回调函数交给主线程执行。整个过程以事件循环机制连接;我只能强行解释到这了,如果还想再权威一下,可以到下面的链接看一下。

此图来自阮一峰的网络日志http://www.ruanyifeng.com/blog/2014/10/event-loop.html

这是之前所理解的事件循环机制,但今天看到的文章,从任务的角度统一了Browser与nodejs的循环机制(但是还是存在差异的),对于上下两种对机制的阐述,应该说下面的阐述是对事件队列的进一步拆分。

js语言为单线程,为了不使执行缓慢的任务阻塞后续任务的执行,所以将js的任务分为同步任务、异步任务;

同步任务在函数调用栈内执行,异步任务则需要借助任务队列,一个线程中调用栈只有一个,但任务队列可以有多个;

1、任务队列又分为macro-task(宏任务)与micro-task(微任务),在最新标准中,它们被分别称为task与jobs。

2、macro-task大概包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。

3、micro-task大概包括: process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

4、setTimeout/Promise等我们称之为任务源。而进入任务队列的是他们指定的具体执行任务。

5、来自不同任务源的任务会进入到不同的任务队列。其中setTimeout与setInterval是同源的。

6、事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的micro-task。当所有可执行的micro-task执行完毕之后。循环再次从macro-task开始,找到其中一个任务队列执行完毕,然后再执行所有的micro-task,这样一直循环下去。

7、其中每一个任务的执行,无论是macro-task还是micro-task,都是借助函数调用栈来完成。

其中Browser与nodejs的差异就体现在第六条:

Browser端严格遵循第六条,执行完一个宏任务就会去检查微任务队列是否有需要执行的微任务,即使微任务内嵌套微任务,也会将嵌套的微任务执行完毕后(这点上nodejs与browser是相同的,对应的就是清空微任务的队列),再去宏任务队列执行下一个宏任务;

nodejs端则会将同源的任务放在一起执行,如果涉及到同源宏任务的嵌套,仍会将同源任务放在一起,但是内部的任务会放在下一次事件循环时执行。若还不理解可以看下面的图片:

这是在script整段代码执行完毕后,还未清空微任务队列时的图示,从图中可以看出相同任务源会放在一个队列执行,执行一个宏任务意味着将该队列执行完毕,当然除去嵌套内部的任务源,举个栗子,如果timeoutTwo中嵌套了一个timeoutThree,timeThree仍就会放在setTimeout队列内,但是不会与one、two一起被执行,而是等到下一次事件循环时才会执行;还有setInterval也会与setTimeout放在一起(因为他们算作相同的任务源,只是setTimeout执行一次,setInterval执行多次);

重点在于理解事件循环结束节点,一次宏任务执行完毕再将微任务队列清空为一个事件循环节点;

本文所举例子,只是设置setTimeout延时为0,它会立即进入任务队列,假如设置更高的延时,还有设置了setInterval则会更复杂,还会存在更多的不确定性,所以需要多次实验分析把原理搞透,才能在使用这些异步api时能够游刃有余;

本文测试所使用的的Browser为chrome 73.0.3683.75,nodejs版本为v8.12.0

最后再放一个栗子,将代码放在浏览器与nodejs中就会发现不同,这就是两个环境之间的差异

'use strict';
    console.log(1);
    setTimeout(() => {
        console.log(2)
        new Promise((resolve) => {
            console.log(6);
            resolve(7);
        }).then((num) => {
            console.log(num);
        })

    });
    setTimeout(() => {
        console.log(3);
           new Promise((resolve) => {
            console.log(9);
            resolve(10);
        }).then((num) => {
            console.log(num);
        })
        setTimeout(()=>{
        	console.log(8);
        })
    })
    new Promise((resolve) => {
        console.log(4);
        resolve(5)
    }).then((num) => {
        console.log(num);
        new Promise((resolve)=>{
        	console.log(11);
        	resolve(12);
        }).then((num)=>{
        	console.log(num);
        })
    })

运行结果如下

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值