以下内容参考 极客时间 - <<浏览器工作原理与实践>> 专栏
宏任务
前面我们已经介绍过了,页面中的大部分任务都是在主线程上执行的,这些任务包括了:
- 渲染事件(如解析 DOM、计算布局、绘制);
- 用户交互事件(如鼠标点击、滚动页面、放大缩小等);
- JavaScript 脚本执行事件;网络请求完成、文件读写完成事件。
为了协调这些任务有条不紊地在主线程上执行,页面进程引入了消息队列和事件循环机制,渲染进程内部会维护一个消息队列。然后主线程采用一个 for 循环,不断地从这些任务队列中取出任务并执行任务。我们把这些消息队列中的任务称为宏任务。消息队列中的任务是通过事件循环系统来执行的
宏任务的问题
宏任务的时间粒度比较大,执行的时间间隔是不能精确控制的,对一些高实时性的需求就不太符合了,比如监听 DOM 变化的需求
微任务
微任务就是一个需要异步执行的函数,是在主函数执行结束之后、当前宏任务结束之前执行
微任务怎么产生的
- 使用
MutationObserver
监控某个 DOM 节点,然后再通过 JavaScript 来修改这个节点,或者为这个节点添加、删除部分子节点,当 DOM 节点发生变化时,就会产生 DOM 变化记录的微任务。 - 使用
Promise
,当调用Promise.resolve()
或者Promise.reject()
的时候,会产生微任务。当然, 作为语法糖的async
/await
也会产生, 参考 MDN async 函数
微任务何时被执行
在当前宏任务中的 JavaScript 快执行完成时,也就在 JavaScript 引擎准备退出全局执行上下文并清空调用栈的时候,JavaScript 引擎会检查全局执行上下文中的微任务队列,然后按照顺序执行队列中的微任务
总结:
以上就是微任务的工作流程,从上面分析我们可以得出如下几个结论:
- 微任务和宏任务是绑定的,每个宏任务在执行时,会创建自己的微任务队列。
- 微任务的执行时长会影响到当前宏任务的时长。比如一个宏任务在执行过程中,产生了 100 个微任务,执行每个微任务的时间是 10 毫秒,那么执行这 100 个微任务的时间就是 1000 毫秒,也可以说这 100 个微任务让宏任务的执行时间延长了 1000 毫秒。所以你在写代码的时候一定要注意控制微任务的执行时长
- 在一个宏任务中,分别创建一个用于回调的宏任务和微任务,无论什么情况下,微任务都早于宏任务执行。
牛刀小试
请说出运行结果并解释流程
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 start')
await async3()
console.log('async2 end')
}
async function async3() {
console.log('async3 start')
}
setTimeout(() => {
console.log('setTimeout')
},0)
console.log('script start')
async1()
new Promise((resolve) => {
console.log('promise1')
setTimeout(resolve, 0)
}).then(() => {
console.log('promise1 then')
})
new Promise((resolve) => {
console.log('promise2')
resolve()
}).then(() => {
console.log('promise2 then')
})
console.log('script end')
关键逻辑分析
async function async1() {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2() {
console.log('async2 start')
await async3()
console.log('async2 end')
}
async function async3() {
console.log('async3 start')
}
async1()
等价的 Promise 写法为
let async1 = () => {
console.log('async1 start')
async2().then(() => {
console.log('async1 end')
})
}
let async2 = () => {
console.log('async2 start')
return async3().then(() => {
console.log('async2 end')
})
}
let async3 = () => {
console.log('async3 start')
return Promise.resolve()
}
async1()
再次整理
let async1 = () => {
console.log('async1 start')
console.log('async2 start')
console.log('async3 start')
return Promise.resolve() // 产生微任务1, 入队任务为then的的回调
.then(() => {
console.log('async2 end')
// 回调返回值被 resolve(undefined) // 产生微任务3, 入队任务为then的的回调
})
.then(() => {
console.log('async1 end')
})
}
async1()
new Promise((resolve) => {
console.log('promise2')
resolve() // 产生微任务2, 入队任务为then的的回调
}).then(() => {
console.log('promise2 then')
})
楼主残忍的关闭了评论