携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
异步执行的函数主要考察的是要对宏任务和微任务进行详细的了解,这小文章介绍了宏任务和微任务的执行的思路。 如果喜欢(❤ ω ❤)点个赞吧!
概念
console.log('hi')
setTimeout(function(){
console.log('cbl')
},1000)
console.log('bye')
在JS引擎和Node环境中存在着一个事件轮询的实践,在Call Stack中是宏任务所决定的执行的顺序,当遇到异步执行的事件时就会产生宏任务事件。注意: 在刚开始声明时整个js代码刚开始就是一个宏任务,是进入事件轮询的基础。其中微任务是宏任务的一个子集,比如在steTimeout事件中,可能存在着微任务,当执行一个宏任务时,在同步任务执行完成之后,就要检查是否在这个宏任务中存在微任务,当当前的宏任务执行完成之后才会去执行下一个宏任务。
执行的顺序为:同步任务--->微任务--->下一个宏任务
分类
宏任务:
- I/O
- setTimeout
- setInterval
- setImmediate
- requestAnimatonFrame
微任务:
- process.nextTick
- MutationObserver
- Promise.then catch finally
练习题
1
在Promise中除了resovle和reject调用, 其他的代码操作都是同步任务。Promise的状态只能改变一次,并且在链式调用then时就会对前一次调用的就结果进行依赖,并且then方法返回的也是一个Promise对象
const promise = new Promise((resolve,reject)=>{
console.log(1);
resolve();
console.log(2);
reject()
})
setTimeout(()=>{console.log(5)},0)
promise.then(()=>{console.log(3)})
.then(()=>{console.log(6)})
.catch(()=>{console.log(7)})
console.log(4)
答案是1,2,4,3,6,5
注解: 先执行同步代码,在执行微任务对列,在执行宏任务队列。promise.then属于微任务,setTimeout属于宏任务。另外promise状态只改变一次,所以catch不执行。
首先new Promise时候打印1和2,因为new Promise时候会立即执行传入的方法 然后后面代码都是异步代码,先将setTimeout的回调加入宏任务队列,再把promise.then放入到微任务队列,然后直接执行最后一句,打印4。 这样宏任务代码执行完了,接下来开始执行微任务队列中的任务,由于promise resolve,因为promise resolve之后状态不会再改变,因此不会执行到reject的对调,所以打印3和6 微任务队列为空,再到宏任务队列中查找任务,找到setTimeout回调执行,打印5 调用栈、宏任务队列、微任务队列都为空,代码执行结束。
2
const first = () => (new Promise((resolve, reject) => {
console.log(3);
let p = new Promise((resolve, reject) => {
console.log(7);
setTimeout(() => {
console.log(5);
resolve();
}, 0);
resolve(1);
});
resolve(2);
p.then((arg) => {
console.log(arg);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
参考答案:3, 7, 4, 1, 2, 5
- 首先定义first
- 然后执行first,然后执行new Promise传入的方法,先打印3
- 又new Promise,执行其中传入的方法,打印7
- 执行setTimeout,将回调放入宏任务队列
- 执行resolve(1),将内部promise状态置为fullfilled,值为1
- 执行resolve(2),将外部promise状态置为fullfilled,值为2
- 执行内部promise.then方法,将回调加入微任务队列
- 执行first().then,即外部的promise,将回调加入到微任务队列
- 调用栈为空,开始从微任务队列拿取任务,首先拿到内部promise的回调,打印其值1
- 然后从微任务队列中拿取外部的promise的回调,打印其值2
- 此时微任务队列为空,开始从宏任务队列中拿取任务,即setTimeout回调,打印5。
- 调用栈,宏任务队列和微任务队列都为空,执行结束。
3
console.log(1);
new Promise(resolve => {
resolve();
console.log(2);
}).then(() => {
console.log(3);
})
setTimeout(() => {
console.log(4);
}, 0);
console.log(5);
参考答案:1,2,5,3,4
- 先打印1
- 执行new Promise的函数,打印2
- 执行promise.then,将回调加入微任务队列
- 将setTimeout的回调加入宏任务队列
- 打印5
- 调用栈为空,取微任务队列中的任务执行,打印3
- 微任务队列为空,取宏任务队列任务执行,打印5
- 调用栈、微任务队列、宏任务队列都为空,执行结束
4
Promise.resolve()
.then(() => {
console.log('1');
})
.then(() => {
console.log('2');
});
setTimeout(() => {
Promise.resolve()
.then(() => {
console.log('3');
})
.then(() => {
console.log('4');
});
setInterval(() => {
console.log('5');
}, 3000);
console.log('6');
}, 0);
参考答案:1,2,6,3,4,5,5...
- 先执行Promise.resolve,将两个回调加入到微任务队列中
- 执行setTimeout,将其回调加入宏任务队列
- 调用栈为空,拿出微任务队列中的两个回调执行,打印1,2
- 微任务队列为空,拿出宏任务队列中的setTimeout的回调执行
- 将setTimeout中的Promise.resolve的两个回调加入到微任务队列
- 将setTimeout中的setInterval的回调加入宏任务队列
- 打印6
- 取出微任务队列中的两个Promise的回调,打印3,4
- 取宏任务队列中的setInterval的回调执行,每隔3s符合执行条件,打印5。注意setInterval调用时候不马上会执行一次,第一次执行是3s以后。
5
setTimeout(function() {
console.log(1);
}, 0);
console.log(2);
async function s1() {
console.log(7)
await s2();
console.log(8);
}
asycn function s2() {
console.log(9);
}
s1();
new Promise((resolve, reject) => {
console.log(3);
resolve();
console.log(6);
}).then(() => console.log(4))
console.log(5);
参考答案:2,7,9,3,6,5,8,4,1
- 记住async只是promise的语法糖,转化为等价的形式就好分析了
- 先执行setTimeout,加入宏任务队列中
- 打印2
- 执行s1,同步打印7
- 执行s2,同步打印9
- 执行完s2,将console.log(8)加入到微任务队列
- 然后执行s1后面的Promise,打印3和6
- 执行then,将console.log(4)加入到微任务队列中
- 打印5
- 调用栈为空,将微任务队列中的两个任务依次拿出来执行,打印8和4
- 微任务队列执行完,将宏任务队列的任务拿出来执行,打印1
- 调用栈、微任务队列、宏任务队列都为空,执行完毕。
6
<script>
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
</script>