异步执行顺序——宏任务与微任务不同环境下的出队规则

导读

javascript是一门单线程语言,一切javascript版的多线程都是用单线程模拟出来的,所以代码执行还是顺序执行的原则,只不过编写的顺序被执行环境重新“编排”了一下而已。

执行过程中,我们把任务分为同步任务和异步任务,而异步任务又分为宏任务,微任务。
一般顺序:同步->微任务->宏任务

  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval
  • micro-task(微任务):Promise,process.nextTick

node环境与浏览器环境区别:(这里先写出区别,下面做详细介绍,具体不同环境为什么异步输出不同。)

node环境中,任务全部出队,并执行。

浏览器环境,出队一个任务,并执行。

任务入队规则

异步任务队列分为宏任务队列,微任务队列。

代码顺序执行,碰到异步代码后放入对应队列内。第一遍代码执行完毕后,会取出微任务队列内的代码继续执行,执行完后再取出宏任务队列内的代码继续执行。以此反复执行直到最后执行完所有任务。

运用递归思想描述

function 代码执行(任务){
	任务执行,异步的代码入队
	代码执行(微任务出队)
	代码执行(宏任务出队)
}

这里说明一下:由于node环境下的事件监听依赖libuv与前端环境不完全相同,输出会有差异。

主要原因:虽然规则相同,但是入队的顺序会有差异,出队任务数量也有不同,所以输出会有差异

实例演示

下面结合实例,将在node端和浏览器端分别说明其差异性。

使用代码:

    console.log('1');
    setTimeout(function() { //timeout1
        console.log('2');
        new Promise(function(resolve) {
            console.log('4');
            setTimeout(function(){//timeout2
                console.log('5');
                resolve()//promise1
            })
        }).then(function() {
            console.log('6');
        })
    })
    new Promise(function(resolve) {
        console.log('8');
        resolve();//promise2
    }).then(function() {
        console.log('9');
    })
    setTimeout(function() { //timeout3
        console.log('10');

        new Promise(function(resolve) {
            console.log('12');
            resolve();//promise3
        }).then(function() {
            console.log('13');
        })
        setTimeout(function(){//timeout4
            console.log('14')
            new Promise(function(resolve){
                console.log('15')
                resolve() //promise4
            }).then(function() {
                console.log('16');
            })
        })
    })
浏览器端:

这里建议打开两个页面,一边对照代码一边看一下步骤。

第一次执行代码:输出1,timeout1压入宏队列,输出8,promise2压入微队列,timeout3压入宏任务队列。

//在浏览器端,边执行代码,边把异步代码加入对应队列,这一次执行都是同步代码。

//宏任务队列内[timeout1,timeout3],微任务队列[promise2]

第二次执行代码(promise2出队):输出9

//微任务队列优先级高,先执行微任务,浏览器端只有一个任务出队并执行。

//宏任务队列内[timeout1,timeout3],微任务队列[]

第三次执行代码(timeout1出队):输出2,输出4,timeout2压入宏队。

//宏任务队列内[timeout3,timeout2],微任务队列[]

第四次执行代码(timeout3出队):输出10,输出12,promise3压入微队列,timeout4压入宏队列。

//宏任务队列内[timeout2,timeout4],微任务队列[promise3]

第五次执行代码(promise3出队):输出13。

//宏任务队列内[timeout2,timeout4],微任务队列[]

第六次执行代码(timeout2出队):输出5,promise1压入微队列。

//宏任务队列内[timeout4],微任务队列[promise1]

第七次执行代码(promise1出队):输出6。

//宏任务队列内[timeout4],微任务队列[]

第八次执行代码(timeout4出队):输出14,输出15,promise4压入微队列。

//宏任务队列内[],微任务队列[promise4]

第九次执行代码(promise4出队):输出16。

//宏任务队列内[],微任务队列[]

顺序为:1,8,9,2,4,10,12,13,5,6,14,15,16

node端

第一次执行代码:输出1,timeout1压入宏队列,输出8,promise2压入微队列,timeout3压入宏任务队列。

//第一次执行node端和浏览器端二者相同。

//宏任务队列内[timeout1,timeout3],微任务队列[promise2]

第二次执行代码(promise2出队):输出9

//二者相同。

//宏任务队列内[timeout1,timeout3],微任务队列[]

第三次执行代码([timeout1,timeout3出队):输出2,输出4,timeout2压入宏队列,输出10,输出12,promise3压入微队列,timeout4压入宏队列。

//这里我们可以看到,node与浏览器不同之处,timeout1,timeout3一起出队,所以内部的同等级(同步代码)就一起执行了。node环境的这一次执行完成了浏览器多次执行步骤。

//宏任务队列内[timeout2,timeout4],微任务队列[promise3]

第四次执行代码(promise3出队):输出13。

//宏任务队列内[timeout2,timeout4],微任务队列[]

第五次执行代码(timeout2,timeout4出队):输出5,promise1压入微队列,输出14,输出15,promise4压入微队列。

//在这里输出数值与浏览器端开始有差异。

//宏任务队列内[],微任务队列[promise1,promise4]

第六次执行代码(promise1,promise4出队):输出6,输出16。

//宏任务队列内[],微任务队列[]

顺序为:1,8,9,2,4,10,12,13,5,14,15,6,16

总结:

简单结构的代码,一般执行顺序:同步->微任务->宏任务。复杂结构代码,微任务优先级大于宏任务优先级。同步代码边执行,边把异步任务入队,然后系统选择出队的任务再一次新一轮执行。

node环境与浏览器环境区别:node环境中,任务全部出队,并执行。浏览器环境,出队一个任务,并执行。

备注1
console.log("11")
new Promise(function(resolve) {
        console.log('12');//这里代码属于同步代码,只有then内代码才是异步代码,同上面“console.log("11")”代码段一起顺序执行。
        resolve();
    }).then(function() {
        console.log('13');
    })
备注2

在node环境中process.nextTick()比Promise优先级高,所以优先执行

new Promise(function(resolve) {
    console.log('7');
    resolve();
}).then(function() {
    console.log('8')
})
process.nextTick(function() {
    console.log('6');
})
//7,6,8
备注3

这一次,彻底弄懂 JavaScript 执行机制:https://juejin.im/post/59e85eebf265da430d571f89

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值