宏任务和微任务
作者:ZH彪
链接:https://www.jianshu.com/p/bcbf7894027c
来源:简书
微任务和宏任务皆为异步任务,它们都属于一个队列,主要区别在于他们的执行顺序,Event Loop的走向和取值。
宏任务和微任务的分配
宏任务 浏览器 Node I/O ✅ ✅ setTimeout ✅ ✅ setInterval ✅ ✅ setImmediate ❌ ✅ requestAnimationFrame ✅ ✅ 微任务 process.nextTick ❌ ✅ MutationObserver ✅ ❌ Promise.then catch finally✅ ✅
宏任务与微任务之间的执行顺序(同步任务->微任务->宏任务)
下面说说执行到宏任务后是怎么继续运行的
(这里声明下,整段js代码就是第一个大的宏任务,事件循环是由这第一个宏任务开始的,然后分出微任务,这里是为了理解微任务宏任务的执行区别就先跳过这第一层)
说一个很有名的银行例子
:银行柜台前排着一条队伍,都是存钱的人,存钱属于宏任务,这条队伍就是宏任务队列,当一个‘宏大爷’被叫到了自己的号码,就上前去--被处理,处理存钱业务时,‘宏大爷’突然想给自己的存款办个微理财(微任务
),那么银行职员就将他的需求添加到自己的微任务队列,大爷就不用再排队了,直接在存钱宏任务进行完后就处理衍生出来的微任务理财,办理财时大爷又说办个信用卡,那就又排到微任务队列里。但要是在此次存钱时‘宏大爷’说他还要存钱,且是他老伴要存钱,也是宏任务
,但不好意思,需要取号到宏任务队列的后面排队(这里就是在宏任务进行时产生微任务和宏任务的处理方式)。
//宏任务1 setTimeout(function () { console.log('1');//宏任务1 }); //Promise new Promise(function (resolve) { console.log('2');//同步任务1 resolve(); }).then(function () { console.log('3');//微任务1 }); //同步任务 console.log('4');//同步任务2 // 一步一步来分析,js可以看成是一个大的宏任务,以上是开始的一段,可以看到按照代码顺序有setTimeout、Promise、Promise.then()、console.log // setTimeout 是宏任务 、Promise是微任务、console.log是同步任务,而Promise是将异步的操作用同步的流程表达出来,所以在Promise里面的是同步任务、.then()里面的是微任务 // 所以按照代码书写顺序和执行顺序 同步任务 > 微任务 > 宏任务,最先执行的是同步任务1,然后是同步任务2、微任务1、宏任务1 //所以到目前为止打印出来的依次是:2 4 3 1 //下面是宏任务2和宏任务3,注意宏任务2里面有很多同步任务和微任务,这些都是包括在宏任务2里面的 //宏任务2 setTimeout(function () { console.log('5');//宏任务2中的同步任务 new Promise(function (resolve) { console.log('6');//宏任务2中的同步任务 new Promise(function (resolve) {//宏任务2中的微任务 console.log('x1'); resolve(); }).then(function () { console.log('X2'); }); setTimeout(function () {//宏任务2中的宏任务 console.log('X3'); new Promise(function (resolve) {//宏任务2中的宏任务中的同步任务 console.log('X4'); resolve(); }).then(function () {//宏任务2中的宏任务中的微任务 console.log('X5'); }); }) resolve(); }).then(function () {//宏任务2中的微任务 console.log('7'); }); }) //现在代码走到了宏任务2里面,开始按照顺序执行,首先可以看到宏任务2里面有两块,1个是宏任务2中的同步任务(console.log('6');),1个是 Promise // console.log('5'); 是同步任务,所以最先打印 //宏任务2的第一个Promise里面包含的有:同步任务console.log('6')和Promise、微任务Promise.then、宏任务setTimeout //同步任务 '6'最先打印,然后是Promise里面的同步任务'x1',微任务Promise.then()里面的'x2' //而setTimeout是宏任务,得丢到宏任务列表的最后面去排队(目前的代码最后面排着的是宏任务3,所以这个宏任务得排在宏任务3的后面) //最后执行外面的微任务Promise.then() console.log('7'); //所以这一块的打印顺序为:5 6 x1 x2 7 //宏任务3 setTimeout(function () { console.log('8'); }); // 因为宏任务3里面只有一个同步任务 所以直接输出 8 // 接下来执行宏任务2里面的宏任务 也就是刚被丢出来排在最后面的setTimeout,跟宏任务2的执行顺序同理,所以输出顺序为:x3 x4 x5 //(对于这段代码node环境和浏览器环境输出一致) //最终输出答案:2,4,3,1,5,6,x1,x2,7,8,x3,x4,x5
无注解的原代码
setTimeout(function () {//宏任务1 console.log('1'); }); new Promise(function (resolve) { console.log('2');//同步任务1 resolve(); }).then(function () {//微任务1 console.log('3'); }); console.log('4');//同步任务2 setTimeout(function () {//宏任务2 console.log('5');//宏任务2中的同步任务 new Promise(function (resolve) { console.log('6');//宏任务2中的同步任务 new Promise(function (resolve) {//宏任务2中的微任务 console.log('x1'); resolve(); }).then(function () { console.log('X2'); }); setTimeout(function () {//宏任务2中的宏任务 console.log('X3'); new Promise(function (resolve) {//宏任务2中的宏任务中的同步任务 console.log('X4'); resolve(); }).then(function () {//宏任务2中的宏任务中的微任务 console.log('X5'); }); }) resolve(); }).then(function () {//宏任务2中的微任务 console.log('7'); }); }) setTimeout(function () {//宏任务3 console.log('8'); }); //(对于这段代码node环境和浏览器环境输出一致) //输出答案:2,4,3,1,5,6,x1,x2,7,8,x3,x4,x5