在 javascript 的事件循环中,微任务(microtask)会在当前宏任务(macrotask)的回调执行完毕后、下一次 ui 渲染前被清空。具体做法是:在当前宏任务的同步代码和异步回调结束后,引擎会检查微任务队列,将所有待执行的微任务按顺序执行完毕,直到队列为空,然后才会去处理下一个宏任务或进行页面渲染。

Promise.then 回调为何比 setTimeout 先执行
因为 Promise.then 注册的回调属于微任务,而 setTimeout 注册的回调属于宏任务。当两者在同一轮事件循环中被触发时,宏任务(如定时器到期)进入执行栈,其内部的同步代码执行完后,会优先清空微任务队列,因此 Promise 的回调会比下一个定时器的回调更早执行。

console.log('1');
setTimeout(function() {
console.log('2');
}, 0);
new Promise(function(resolve) {
console.log('3');
resolve();
}).then(function() {
console.log('4');
});
console.log('5');预期输出:
1
3
5
4
2
立即学习“Java免费学习笔记(深入)”;
async/await 中的 await 如何影响执行顺序
await 关键字会暂停当前 async 函数的执行,并将剩余部分注册为微任务。这意味着,当一个 async 函数遇到 await 时,它会立即返回一个未解决的 Promise,并将 await 之后的代码放入微任务队列。这导致 async 函数中 await 之后的代码,总是在当前调用栈中的所有同步代码执行完毕后,且在下一个宏任务开始前执行。

async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function() {
console.log('setTimeout');
}, 0);
async1();
new Promise(function(resolve) {
console.log('promise1');
resolve();
}).then(function() {
console.log('promise2');
});
console.log('script end');预期输出:
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
微任务队列清空期间新产生的微任务如何处理
在微任务队列清空的过程中,如果某个微任务的执行又产生了新的微任务,这些新产生的微任务会被添加到队列末尾,并在当前这一轮“清空”动作中继续被执行,直到队列为空。也就是说,微任务具有“递归”清空的特性,只要还有微任务在产生,事件循环就不会切换到下一个宏任务或渲染阶段。


















