Macrotask 和 Microtask
1. 概述
Macrotask 和 Microtask 表示异步任务的两种分类。
1.1 Macrotask(宏任务)
API | 浏览器支持 | Node 支持 |
---|---|---|
setTimeout | 支持 | 支持 |
setInterval | 支持 | 支持 |
setImmediate | 不支持 | 支持 |
requestAnimationFrame | 支持 | 不支持 |
1.2 Microtask(微任务)
API | 浏览器支持 | Node 支持 |
---|---|---|
process.nextTick | 不支持 | 支持 |
MutationObserver | 支持 | 不支持 |
Promise.then/catch/finally | 支持 | 支持 |
2. 如何理解宏任务和微任务?
简单的理解就是浏览器V8引擎执行代码时首先执行主线程任务,遇到微任务扔进微任务队列,遇到宏任务扔进宏任务队列,主线程队列清空之后就去执行微任务队列,最后执行宏任务队列
几个例子
js
async function async1(){
console.log('1')
await async2()
console.log('2')
}
async function async2(){
console.log('3')
}
console.log('4')
setTimeout(function(){
console.log('5')
},0)
async1();
new Promise(function(resolve){
console.log('6')
resolve();
}).then(function(){
console.log('7')
})
console.log('8')
// >> 4,1,3,6,8,2,7,5
js
//主线程直接执行
console.log('1');
//丢到宏事件队列中
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
//微事件1
process.nextTick(function() {
console.log('6');
})
//主线程直接执行
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
//微事件2
console.log('8')
})
//丢到宏事件队列中
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
// >> 1,7,6,8,2,4,3,5,9,11,10,12
3. 浏览器端插入微任务的几种方法和兼容性
3.1 使用es6语法 promise
js
Promise.resolve().then(callback)
3.2 使用MutationObserver (兼容性最好)
js
function microtask(callback){
let count = 0;
// 通过装饰者模式,将回调函数包装一下,将执行之后的返回值保存起来
const observe = new MutationObserver(callback);
// 为了节约开销,创建一个文本比创建一个dom可划算的多
const textNode = document.createTextNode(String(count));
observe.observe(textNode, {
// 当文本改变时触发回调
characterData: true
});
// 改变文本,回调callback触发
textNode.data = String(++count);
}
3.3 微任务API queueMicrotask(性能开销最小,nodejs支持)
js
queueMicrotask(callback)
使用MessageChannel
js
function microtask(callback) {
const channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}