Wuxh

Front-end Development

0%

JS宏任务和微任务

JS 事件循环机制之宏任务/微任务;

执行打印顺序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);

Promise.resolve()
.then(() => {
console.log('promise1');
})
.then(() => {
console.log('promise2');
});

console.log('script end');

我的错误答案是script start , promise1 , promise2 , script end , setTimeout

正确答案是:script start, script end, promise1, promise2, setTimeout;

因为Promise是微任务,主线程会在同步任务做完后先清空微任务队列,再执行宏任务队列的setTimeout;

宏任务(macro-task)

macro-task

浏览器为了能够使得 JS 内部 task 与 DOM 任务能够有序的执行,会在一个 task 执行结束后,在下一个 task 执行开始前,对页面进行重新渲染 (task->渲染->task->…)
鼠标点击会触发一个事件回调,需要执行一个宏任务,然后解析 HTMl。

setTimeout的作用是等待给定的时间后为它的回调产生一个新的宏任务。
这就是为什么打印 setTimeoutscript end 之后。因为打印 script end 是第一个宏任务里面的事情,而 setTimeout 是另一个独立的任务里面打印的。


微任务(micro-task)

micro-task

微任务通常来说就是需要在当前 task 执行结束后立即执行的任务,比如对一系列动作做出反馈,
或或者是需要异步的执行任务而又不需要分配一个新的 task,这样便可以减小一点性能的开销。

只要执行栈中没有其他的 js 代码正在执行且每个宏任务执行完,微任务队列会立即执行。

如果在微任务执行期间微任务队列加入了新的微任务,会将新的微任务加入队列尾部,之后也会被执行。

微任务包括了mutationobserve的回调还有接下来的例子promise的回调。

一旦一个pormise有了结果,或者早已有了结果(有了结果是指这个promise到了 fulfilled rejected 状态),
他就会为它的回调产生一个微任务,这就保证了回调异步的执行即使这个promise早已有了结果。
所以对一个已经有了结果的promise调用.then(yey, nay)会立即产生一个微任务。
这就是为什么 promise1 , promise2 会打印在 script end 之后,因为所有微任务执行的时候,当前执行栈的代码必须已经执行完毕。

promise1 , promise2 会打印在 setTimeout之前是因为所有微任务总会在下一个宏任务之前全部执行完毕。

参考文档

欢迎关注我的其它发布渠道