宏任务
浏览器中:I/O、setTimeout、setInterval、requestAnimationFrame
Node中:I/O、setTimeout、setInterval、setImmediate
微任务
浏览器中:MutationObserver、Promise.then/.catch/.finally
Node中:process.nextTick 、Promise.then/.catch/.finally
微任务与宏任务执行顺序
JavaScript是单线程的,常用的任务分为同步任务和异步任务。在每轮事件循环中,主线程会先执行完同步任务,再执行异步任务。
整体JavaScript代码将作为一个宏任务执行,先将同步任务进入主线程执行,异步任务进入事件(Event Table)并注册回调函数(如:success、then、catch等)。当异步事件完成,回调函数进入事件队列等待被调用。而来自不同任务源的任务会进入不同的任务队列。其中setTimeout与setInterval是同源的。
js引擎Monitoring Process进程会不断的检查主线程执行栈是否为空,一旦为空,就会检查事件队列中是否有等待被调用的函数,如果有,主线程将依次读取回调函数并调用。否则执行下一轮事件循环。
在每轮事件循环中微任务队列的优先级高于宏任务队列。微任务队列中排队的所有微任务都在同一周期内处理,而这些微任务本身也可以将其他微任务添加到微任务队列中中执行,只有这些微任务全部执行完成时,才会执行下一个宏任务。
(先同步后异步,先微任务,再宏任务)
console.log('script start'); // 同步
setTimeout(function () {
console.log('setTimeout1'); // 异步
}, 300);
setTimeout(function () {
console.log('setTimeout2'); // 异步
}, 0);
new Promise(resolve => {
resolve()
console.log('promise1'); // 同步
}).then(() => {
console.log('promise2'); // 异步
})
console.log('script end'); // 同步
// script start
// promise1
// script end
// promise2
// setTimeout2
// setTimeout1
process.nextTick()先于Promise.then()执行, setTimeout()与setImmediate()执行顺序取决于setTimeout的执行周期与设备性能。
console.log('golbol');
setTimeout(function () {
console.log('timeout1');
process.nextTick(function () {
console.log('timeout1_process');
})
new Promise(function (resolve) {
console.log('timeout1_promise');
resolve();
}).then(function () {
console.log('timeout1_then')
})
})
setImmediate(function () {
console.log('immediate1');
process.nextTick(function () {
console.log('immediate1_process');
})
new Promise(function (resolve) {
console.log('immediate1_promise');
resolve();
}).then(function () {
console.log('immediate1_then')
})
})
process.nextTick(function () {
console.log('process1');
})
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise1_then')
})
setTimeout(function () {
console.log('timeout2');
process.nextTick(function () {
console.log('timeout2_process');
})
new Promise(function (resolve) {
console.log('timeout2_promise');
resolve();
}).then(function () {
console.log('timeout2_then')
})
})
process.nextTick(function () {
console.log('process2');
})
new Promise(function (resolve) {
console.log('promise2');
resolve();
}).then(function () {
console.log('promise2_then')
})
setImmediate(function () {
console.log('immediate2');
process.nextTick(function () {
console.log('immediate2_process');
})
new Promise(function (resolve) {
console.log('immediate2_promise');
resolve();
}).then(function () {
console.log('immediate2_then')
})
})
整体代码作为宏任务执行:
宏任务代码 | 微任务队列 |
整体代码 | |
- 输出:global | |
- 将setTimeout标记为timeout1添加到宏任务队列中 | |
- 将setImmediate标记为immediate1添加到宏任务队列中 | |
- 将process标记为process1添加到微任务队列中 | |
- 输出:promise1。将promise标记为promise1添加到微任务队里 | |
- 将setTimeout标记为timeout2添加到宏任务队列中 | |
- 将process标记为process2添加到微任务队列中 | |
- 输出:promise2。将promise标记为promise2添加到微任务队里 | |
- 将setImmediate2标记为immediate2添加到宏任务队列中 | |
``` | |
global、promise1、promise2 | |
``` |
宏任务队列 | 微任务队列 |
timeout1、timeout2 | process1、process2 |
immediate1、immediate2 | promise1、promise2 |
主进程事件执行完,执行微任务队列,process.nextTick比promise.then先执行
process1、process2、promise1_then、promise2_then
复制代码
执行setTimeout宏任务队列
宏任务队列 | 微任务队列 |
timeout1、timeout2 | |
immediate1、immediate2 |
分别执行宏任务队列setTimeout任务源
输出:timeout1
将process标记为timeout1_process添加到微任务队列中
输出:timeout1promise。将promise标记为timeout1then添加到微任务队里
输出:timeout2
将process标记为timeout2_process添加到微任务队列中
输出:timeout2promise。将promise标记为timeout2then添加到微任务队里
timeout1、timeout1_promise、timeout2、timeout2_promise
复制代码
主进程事件执行完,执行微任务队列
宏任务队列 | 微任务队列 |
timeout1process、timeout2process | |
immediate1、immediate2 | timeout1then、timeout2then |
执行微任务队列:
timeout1_process、timeout2_process、timeout1_then、timeout2_then
复制代码
执行setImmediate宏任务队列
宏任务队列 | 微任务队列 |
immediate1、immediate2 |
分别执行宏任务队列setImmediate任务源
输出:immediate1
将process标记为immediate1_process添加到微任务队列中
输出:immediate1promise。将immediate标记为immediate1then添加到微任务队里
输出:immediate2
将process标记为immediate2_process添加到微任务队列中
输出:immediate2promise。将promise标记为immediate2then添加到微任务队里
immediate1、immediate1_promise、immediate2、immediate2_promise
复制代码
主进程事件执行完,执行微任务队列
宏任务队列 | 微任务队列 |
immediate1process、immediate2process | |
immediate1then、 immediate2then |
执行微任务队列:
immediate1_process、immediate2_process、immediate1_then、immediate2_then
整体输出
global、promise1、promise2、
process1、process2、promise1_then、promise2_then、
timeout1、timeout1_promise、timeout2、timeout2_promise、
timeout1_process、timeout2_process、timeout1_then、timeout2_then、
immediate1、immediate1_promise、immediate2、immediate2_promise、
immediate1_process、immediate2_process、immediate1_then、immediate2_then