在 JavaScript 中,任务分为微任务(microtask)和宏任务(macrotask),它们用于管理代码的执行顺序和优先级。
宏任务是由浏览器 API 提供的异步任务,例如 setTimeout、setInterval、XHR 请求等。宏任务会被添加到任务队列中,在主线程执行栈中的所有任务执行完毕后立即执行。
而微任务是在当前任务执行结束后,紧接着执行的任务。微任务通常是由 Promise 的处理程序、MutationObserver 或者 process.nextTick 等触发的。
下面是它们的执行顺序:
-
首先,JS 运行时会执行同步任务,也就是主线程中的代码,直到执行栈为空。
-
当执行栈为空时,JS 引擎会检查微任务队列。如果微任务队列中有任务,就按照先进先出的顺序依次执行所有微任务,直到微任务队列为空。
-
当微任务队列为空时,JS 引擎会检查宏任务队列。
-
如果宏任务队列中有任务,则从队列中选择一个最早进入的任务执行。执行完该任务后,返回步骤 2,检查微任务队列。
-
当所有微任务和宏任务都执行完毕时,JS 运行时会再次检查是否有新的微任务加入,如果有,则重复步骤 2;如果没有,则等待新的宏任务加入。
需要注意的是,每个宏任务执行完毕后,浏览器会检查页面是否需要进行渲染(重绘和重排)。如果需要渲染,则将进行渲染操作,然后再执行下一个宏任务。
在实际应用中,了解微任务和宏任务的执行顺序对于编写高效的异步代码至关重要。例如,如果你希望在当前宏任务执行结束后立即执行某些代码,可以使用 Promise 的 then、catch 或者 finally 方法来创建微任务。
示例:
console.log('主线程开始');
setTimeout(() => {
console.log('宏任务1');
}, 0);
Promise.resolve().then(() => {
console.log('微任务1');
}).then(() => {
console.log('微任务2');
});
console.log('主线程结束');
输出:
主线程开始
主线程结束
微任务1
微任务2
宏任务1
根据上述示例,我们可以看到微任务总是在当前宏任务执行结束前执行,而且微任务的执行顺序是按照添加的顺序执行的。
通过理解微任务和宏任务的执行机制,你可以更好地控制代码的执行顺序,以满足特定的业务需求。