0、前言
为什么要区分微任务和宏任务?
(1)js是单线程的,但是分同步异步
(2)微任务和宏任务皆为异步任务,它们都属于一个队列
(3)宏任务一般是:script、setTimeout、setInterval、postMessage、MessageChannel、setImmediate(Node.js 环境)
(4)微任务:Promise.then、Object.observe、MutationObserver、process.nextTick(Node.js 环境)
(5)先执行同步再执行异步,异步遇到微任务,先执行微任务,执行完后如果没有微任务,就执行下一个宏任务,如果有微任务,就按顺序一个一个执行微任务
微任务和宏任务是怎么执行的?
执行顺序:先执行同步代码,遇到异步宏任务则将异步宏任务放入宏任务队列中,遇到异步微任务则将异步微任务放入微任务队列中,当所有同步代码执行完毕后,再将异步微任务从队列中调入主线程执行,微任务执行完毕后再将异步宏任务从队列中调入主线程执行,一直循环直至所有任务执行完毕。
这里容易产生一个错误的认识:就是微任务先于宏任务执行。实际上是先执行同步任务然后在执行异步任务,异步任务是分宏任务和微任务两种的。
1、概念
宏任务:指执行栈中待执行的任务,如计时器,事件回调,http回调等。
微任务:指执行栈清空后立即执行的任务,如Promise的回调函数、process.nextTick、MutationObserver等。
2、示例
2.1 例1
代码如下:
console.log("start");
setTimeout(()=>{
console.log("setTimeout");
},0);
new Promise((resolve,reject)=>{
for(var i=0;i<5;i++){
console.log(i);
}
resolve() //修改promise状态为成功
}).then(()=>{
console.log("promise回调函数");
})
console.log("end");
// start, 0, 1, 2, 3, 4, end, promise回调函数, setTimeout
分析:
1、先执行主线程上的同步代码,输出start
2、遇到setTimeout将其加入到宏任务队列等待执行
3、遇到promise 立即执行,输出 0,1,2,3,4
4、遇到promise的回调函数将其加入到微任务队列
5、执行主线程的同步代码,输出end
6、第一个宏任务队列执行完毕查看存在微任务队列,执行微任务队列中的任务,输出promise的回调函数
7、微任务执行完毕,执行下一个宏任务队列中的任务,输出setTimeout
2.2 例2
代码如下:
console.log(1)
setTimeout(function() {
console.log(2)
}, 0)
const p = new Promise((resolve, reject) => {
resolve(4)
})
p.then(data => {
console.log(data)
})
console.log(3)
//1,3,4,2
分许:
- 遇到同步任务console.log(1),输出1;
- 遇到setTimeout 异步宏任务,放入宏任务队列中;
- 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,但由于new Promise没有输出事件,所以接着执行遇到.then;
- 执行.then,异步微任务,被分发到微任务Event Queue中;
- 遇到同步任务console.log(3),输出3;
- 主线程中同步任务执行完,从微任务队列中取出任务到主线程中,p.then 输出4,微任务执行完毕,任务队列为空;
- 开始执行宏任务setTimeout 输出2,宏任务队列为空;
2.3 例3
代码如下:
console.log(1)
setTimeout(function() {
console.log(2)
new Promise(function(resolve) {
console.log(3)
resolve()
}).then(function() {
console.log(4)
})
})
new Promise(function(resolve) {
console.log(5)
resolve()
}).then(function() {
console.log(6)
})
setTimeout(function() {
console.log(7)
new Promise(function(resolve) {
console.log(8)
resolve()
}).then(function() {
console.log(9)
})
})
console.log(10)
//1,5,10,6,2,3,4,7,8,9
分析:
- 遇到同步任务console.log(1),输出1;
- 遇到setTimeout 异步宏任务,放入宏任务队列中;
- 遇到 Promise,new Promise在实例化的过程中所执行的代码都是同步进行的,所以输出5,所以接着执行遇到.then;
- 执行.then,异步微任务,被分发到微任务Event Queue中;
- 遇到setTimeout,异步宏任务;放入宏任务队列中;
- 遇到同步任务console.log(10),输出10,主线程中同步任务全部执行完;
- 从微任务队列中取出任务到主线程中,输出6;
- 在从宏任务队列中取出任务到主线程中,执行第一个setTimeout,输出2,3,4(在宏任务中执行同步,同步,异步微任务);
- 在执行第二个setTimeout,输出7,8,9(和8同理);
2.4 例4
代码如下:
console.log('1');
async function async2() {
console.log('2')
}
async2()
console.log('3');
// 1,2,3
分析:
- 遇到同步任务,输出1;
- 遇到async await会暂停当前执行,输出2;
- 遇到同步任务,输出3.
2.5 例5
代码如下:
console.log('1');
async function async1() {
await async2();
console.log('2');
};
async function async2() {
console.log('3')
}
console.log('4');
async1()
分析:
- 遇到同步任务,输出1;
- 遇到async await,但函数并未执行;
- 遇到同步任务,输出4;
- 执行async1,输出3, 2。