跳到主要内容

谈谈微任务和宏任务

八股文上谈​

js是单线程的,一次只能执行一个任务,通过事件循环机制,可以处理多个异步任务操作,而异步任务队列包含有宏任务队列和微任务队列 宏任务的事件循环每次只能处理一个,处理完就必须进行下一次循环;而微任务则是一次事件循环可以处理所有的微任务。 现在nodejs和浏览器 事件循环执行的顺序的行为统一了,也就是每一个宏任务执行完毕之后都必须将当前的微任务队列清空,才会执行下一个宏任务 宏任务包括:script(全局任务),setTimeout,setInterval,setImmediate(Node.js环境),I/O,UI rendering。

微任务包括:Promise,Promise.then, process.nextTick(Node.js环境),MutationObserver(浏览器环境)

虽然宏任务在事件循环中是先执行的,但微任务会在当前宏任务执行完毕后立即执行,这样微任务能够优先于下一个宏任务执行,从而保证了微任务的优先级高于宏任务。这种机制可以确保微任务能够及时执行,而不会被延迟到下一个事件循环中。而宏任务是在事件循环的下一个循环中执行的

理解上谈​

  • 其实宏任务是没有的,为了方便理解,与这个微任务队列区分,就把taskQueue任务队列理解为宏任务队列

  • v8引擎: 有mircoTaskQueue 【微任务队列】

  • 宿主环境: taskQueue【任务=>理解为宏任务队列】

  • V8 引擎内部会维护一个微任务队列(Microtask Queue),用于存放 JavaScript 引擎内部产生的微任务,比如 Promise 的回调函数、MutationObserver 的回调函数等。微任务会在当前宏任务执行完毕后立即执行,这保证了微任务能够优先于下一个宏任务执行

代码输出题目

js
/**
* 宏任务(taskQueue) 在v8引擎中,指的是taskQueue任务队列
* 第一个宏任务队列: script脚本 => start、end
* 第二个宏任务队列: 第一个定时器回调 => children2
* 第三个宏任务队列: 第二个定时器回调 => children3
*
* 微任务(microQueue)
* 第一个微任务队列: Promise.resolve().then=> children1
* 第二个微任务队列: Promise.resolve().then=> children2-1
* 第三个微任务队列: Promise.resolve().then=> children3-1
*
* 首先输出 start 、 end 第一个宏任务执行完, 开始执行微任务 ,输出children1, 执行下一个宏任务定时器的回调输出 children2,开始执行微任务,输出children2-1,执行宏任务定时器的回调输出 children3,开始执行微任务,输出children3-1
*/
function test () {
console.log('start')
setTimeout(() => {
console.log('children2')
Promise.resolve().then(() => {console.log('children2-1')})
}, 0)
setTimeout(() => {
console.log('children3')
Promise.resolve().then(() => {console.log('children3-1')})
}, 0)
Promise.resolve().then(() => {console.log('children1')})
console.log('end')
}

test()

// start 、 end 宏任务执行完
// children1 开始执行微任务
// children2 执行下一个宏任务定时器的回调输出 children2
// children2-1 开始执行微任务,输出children2-1
// children3 执行下一个宏任务定时器的回调输出 children3
// children3-1 开始执行微任务,输出children3-1

new Promise(resolve=>{
console.log('1');
setTimeout(()=>{
console.log('2');
resolve()
console.log('3');
})
}).then(()=>{
console.log('4');
})
console.log('5');

// 遇到new Promise会立即执行,输出1,5, 第一个宏任务执行完, 检查微任务,发现微任务的then是嵌套在定时器的回调的,需要执行下一个宏任务定时器才能执行这个微任务,所以输出2、3,最后4
// 输出 1、5、2、3、4