宏任务与微任务
宏任务与微任务的概念
宏任务和微任务是JavaScript中任务队列的两种类型,它们决定了代码的执行顺序。宏任务包括script整体代码、setTimeout、setInterval、I/O操作、UI渲染等;微任务则包括Promise.then、process.nextTick、MutationObserver等。理解它们的区别对于掌握JavaScript异步编程至关重要。
事件循环中的执行顺序
JavaScript引擎在执行代码时遵循特定顺序:
- 执行一个宏任务(通常是script整体代码)
- 执行过程中遇到微任务就加入微任务队列
- 宏任务执行完毕后立即执行所有微任务
- 进行UI渲染(浏览器环境)
- 开始下一个宏任务
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
// script end
// promise1
// promise2
// setTimeout
Node.js中的process.nextTick
在Node.js环境中,process.nextTick创建的微任务优先级高于Promise.then:
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// 输出顺序:
// nextTick
// promise
宏任务与微任务的嵌套
当微任务中产生新的微任务时,会继续执行直到微任务队列清空:
function microtaskLoop() {
Promise.resolve().then(() => {
console.log('microtask');
microtaskLoop(); // 无限循环
});
}
microtaskLoop();
setTimeout(() => console.log('timeout'), 0);
// 会不断输出'microtask',永远不会执行timeout
浏览器与Node.js的差异
不同环境下宏任务和微任务的实现有差异:
-
浏览器环境:
- 宏任务:setTimeout、setInterval、requestAnimationFrame、I/O、UI渲染
- 微任务:Promise.then、MutationObserver
-
Node.js环境:
- 宏任务:setTimeout、setInterval、setImmediate、I/O
- 微任务:process.nextTick、Promise.then
// Node.js中的执行顺序示例
setImmediate(() => console.log('immediate'));
setTimeout(() => console.log('timeout'), 0);
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// 可能的输出顺序:
// nextTick
// promise
// timeout
// immediate
实际应用场景
- 批量DOM更新:
// 使用微任务批量处理DOM更新
function batchUpdate() {
let pending = false;
const callbacks = [];
return function(callback) {
callbacks.push(callback);
if (!pending) {
pending = true;
Promise.resolve().then(() => {
const copies = callbacks.slice();
callbacks.length = 0;
pending = false;
copies.forEach(cb => cb());
});
}
};
}
const update = batchUpdate();
update(() => console.log('Update 1'));
update(() => console.log('Update 2'));
- 优先级控制:
// 确保关键任务优先执行
function criticalTask() {
process.nextTick(() => {
console.log('Critical task');
});
}
function normalTask() {
Promise.resolve().then(() => {
console.log('Normal task');
});
}
criticalTask();
normalTask();
// 输出:
// Critical task
// Normal task
性能考量
- 微任务堆积:过多的微任务会导致主线程阻塞
- 任务拆分:长时间运行的任务应该拆分为多个宏任务
- 优先级选择:根据场景选择合适的任务类型
// 不好的实践 - 微任务无限循环
function badPractice() {
Promise.resolve().then(badPractice);
}
// 好的实践 - 使用宏任务拆分
function goodPractice() {
// 处理一部分工作
if (moreWork) {
setTimeout(goodPractice, 0);
}
}
常见误区
- 认为setTimeout(fn, 0)会立即执行:实际上它只是尽快加入宏任务队列
- 忽略微任务的优先级:微任务会在下一个宏任务之前全部执行完毕
- 混淆Node.js和浏览器的实现差异:特别是process.nextTick和setImmediate的行为
// 误区示例
console.log('start');
setTimeout(() => {
console.log('timeout 1');
Promise.resolve().then(() => console.log('promise 1'));
}, 0);
setTimeout(() => {
console.log('timeout 2');
Promise.resolve().then(() => console.log('promise 2'));
}, 0);
console.log('end');
// 实际输出:
// start
// end
// timeout 1
// promise 1
// timeout 2
// promise 2
高级应用模式
- 任务调度器:
class TaskScheduler {
constructor() {
this.macroTasks = [];
this.microTasks = [];
this.isProcessing = false;
}
addMacroTask(task) {
this.macroTasks.push(task);
this.schedule();
}
addMicroTask(task) {
this.microTasks.push(task);
this.schedule();
}
schedule() {
if (this.isProcessing) return;
this.isProcessing = true;
Promise.resolve().then(() => {
// 先执行所有微任务
while (this.microTasks.length) {
const task = this.microTasks.shift();
task();
}
// 然后执行一个宏任务
if (this.macroTasks.length) {
const task = this.macroTasks.shift();
task();
}
this.isProcessing = false;
if (this.microTasks.length || this.macroTasks.length) {
this.schedule();
}
});
}
}
- 异步队列控制:
async function processTasks(tasks, concurrency = 4) {
const results = [];
let index = 0;
async function runNext() {
if (index >= tasks.length) return;
const currentIndex = index++;
const task = tasks[currentIndex];
results[currentIndex] = await task();
await Promise.resolve(); // 微任务点
await runNext();
}
const workers = Array(concurrency).fill().map(runNext);
await Promise.all(workers);
return results;
}
调试技巧
- 使用console.log定位执行顺序
- 利用Chrome DevTools的Performance面板
- Node.js中使用--trace-event-categories参数
// 调试示例
function debugFlow() {
console.log('同步代码开始');
setTimeout(() => {
console.log('宏任务1');
Promise.resolve().then(() => console.log('宏任务1的微任务'));
}, 0);
Promise.resolve().then(() => {
console.log('微任务1');
setTimeout(() => console.log('微任务1的宏任务'), 0);
});
console.log('同步代码结束');
}
debugFlow();
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:代理服务器配置