宏任务&微任务【前端基础】

一、JavaScript 中的宏任务与微任务

1. 事件循环机制

JavaScript 是单线程语言,通过事件循环处理异步任务。其核心规则是:

  • 执行顺序:同步代码 → 微任务 → 渲染(如需要)→ 宏任务 → 循环。
  • 微任务优先级:每个宏任务执行后,必须清空所有微任务队列,再执行下一个宏任务。
2. 宏任务与微任务分类
  • 宏任务setTimeoutsetIntervalI/OUI渲染script标签整体代码。
  • 微任务Promise.then/catch/finallyMutationObserverqueueMicrotask
3. 执行示例
console.log('Script start'); // 同步
setTimeout(() => console.log('setTimeout'), 0); // 宏任务
Promise.resolve().then(() => console.log('Promise')); // 微任务
console.log('Script end'); // 同步


// 输出顺序:
// Script start → Script end → Promise → setTimeout

JavaScript

解析

  1. 同步代码先执行。
  2. Promise微任务在同步代码后立即执行。
  3. setTimeout宏任务在微任务队列清空后执行。

二、Node.js 中的宏任务与微任务

1. 事件循环差异

Node.js 的事件循环分为多个阶段:

  1. Timers:处理 setTimeoutsetInterval
  2. I/O Callbacks:执行系统操作回调。
  3. Poll:等待新I/O事件。
  4. Check:执行 setImmediate
  5. Close:处理关闭事件(如 socket.on('close'))。
2. 微任务优先级
  • process.nextTick:优先级最高,在事件循环各阶段切换前执行。
  • Promise.then:次优先级,在 process.nextTick 之后执行。
3. 执行示例
console.log('Start');
setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));
Promise.resolve().then(() => console.log('Promise'));
process.nextTick(() => console.log('nextTick'));


// 输出顺序(可能):
// Start → nextTick → Promise → setTimeout → setImmediate

JavaScript

解析

  1. process.nextTickPromise 微任务优先于宏任务。
  2. setTimeoutsetImmediate 的执行顺序可能不稳定(因事件循环阶段差异)。

三、关键差异与注意事项

1. 执行顺序差异
  • JavaScript(浏览器) :微任务在宏任务前执行。
  • Node.jsprocess.nextTick 优先级高于 Promise,且 setImmediate 设计为 Check 阶段专用。
2. 常见误区
  • 嵌套任务:微任务中再添加微任务会阻塞事件循环,需避免无限递归。
  • 渲染时机:浏览器中宏任务后可能触发渲染,而 Node.js 无此阶段。
3. 性能优化建议
  • 微任务:适合高频轻量操作(如状态更新)。
  • 宏任务:适合耗时操作(如批量数据处理)。

四、综合对比示例(Node.js)

console.log('Main');
setTimeout(() => {
  console.log('Timeout 1');
  process.nextTick(() => console.log('nextTick in Timeout'));
}, 0);
setImmediate(() => {
  console.log('Immediate 1');
  Promise.resolve().then(() => console.log('Promise in Immediate'));
});
Promise.resolve().then(() => console.log('Promise 1'));
process.nextTick(() => console.log('nextTick 1'));


// 输出顺序:
// Main → nextTick 1 → Promise 1 → Timeout 1 → nextTick in Timeout → Immediate 1 → Promise in Immediate

JavaScript

解析

  1. process.nextTickPromise 先执行。
  1. setTimeout 回调作为宏任务执行后,其内部的 process.nextTick 仍优先于外层 setImmediate

总结

  • JavaScript:微任务优先于宏任务,适合快速响应异步操作。
  • Node.jsprocess.nextTick 优先级最高,事件循环阶段划分更复杂,需注意 setImmediatesetTimeout 的触发时机差异。合理利用任务类型可优化代码性能,避免阻塞事件循环。

其他补充说明

一、JavaScript 中的事件循环模型增强说明
  1. 运行时架构
  • 调用栈(Call Stack) :同步代码按顺序压入栈中执行,函数调用形成栈帧
  • 堆(Heap) :存储对象等动态分配的内存区域
  • 队列(Queue) :包含宏任务队列和微任务队列,采用先进先出原则
  1. 事件循环完整流程
  • 执行一个宏任务(通常从任务队列中获取)
  • 执行过程中遇到微任务时,将其加入微任务队列
  • 当前宏任务执行完毕后,立即清空微任务队列
  • 执行渲染操作(浏览器环境下)
  • 从宏任务队列中取下一个任务执行
  1. 渲染时机细节
  • 浏览器每16.6ms(约60Hz刷新率)会尝试渲染
  • 渲染前会执行requestAnimationFrame回调(属于宏任务)
  • 微任务执行时机在渲染之前,保证数据更新及时反映到UI
二、Node.js事件循环阶段补充说明
  1. 完整阶段序列
graph LR
A[Timers] --> B[Pending I/O]
B --> C[Idle/Prepare]
C --> D[Poll]
D --> E[Check]
E --> F[Close]
F --> A

Mermaid

  1. 阶段详解
  • Timers阶段:执行setTimeout/setInterval到期回调
  • Poll阶段
  • 计算阻塞时间(基于最快到期定时器)
  • 执行I/O事件回调(文件/网络操作)
  • 队列为空时检查Check阶段是否有setImmediate
  • Check阶段:专门处理setImmediate回调
  1. 特殊API对比
API类型执行阶段优先级
process.nextTick微任务阶段切换前立即执行最高
Promise.then微任务阶段切换后执行次于nextTick
setImmediate宏任务Check阶段常规宏任务
setTimeout(0)宏任务Timers阶段可能晚于setImmediate
三、关键机制差异的深度解析
  1. 定时器精度差异
  • 浏览器:setTimeout(0)实际延迟≥4ms(HTML5规范)
  • Node.js:setTimeout(0)等价于setTimeout(1),但实际精度依赖系统时钟
  1. 任务饥饿问题
// 危险示例:微任务递归导致宏任务饥饿
function microtaskLoop() {
  Promise.resolve().then(() => {
    console.log('Microtask executed');
    microtaskLoop();
  });
}

JavaScript

  • 浏览器:会强制中断无限微任务链(如Chrome的100万次限制)
  • Node.js:无此保护机制,可能导致事件循环完全阻塞
  1. 跨环境行为差异
  • requestAnimationFrame:浏览器专用,Node.js无等效API
  • setImmediate:Node.js特有,浏览器仅IE11支持
  • MessageChannel:浏览器中可用于创建新宏任务队列
四、性能优化实践建议
  1. 任务类型选择原则
  • <50ms的任务优先使用微任务
  • I/O密集型操作使用宏任务分解
  • 动画更新使用requestAnimationFrame
  1. Node.js优化技巧
// 好的实践:分解CPU密集型任务
function processChunk() {
  // 处理数据块
  if (hasMoreWork) {
    setImmediate(processChunk); // 而非同步递归
  }
}

JavaScript

  1. 浏览器内存管理
  • 及时移除事件监听器
  • 避免在微任务中创建大型对象
  • 使用WeakMap管理DOM引用
五、现代API的整合
  1. 异步迭代器
async function processStream() {
  for await (const chunk of stream) {
    // 处理数据块
  }
}

JavaScript

  1. Web Workers集成
// 主线程
const worker = new Worker('task.js');
worker.postMessage(data);


// Worker线程
self.onmessage = ({data}) => {
  const result = heavyCalculation(data);
  self.postMessage(result);
}

JavaScript

  1. AbortController
const controller = new AbortController();
fetch(url, { signal: controller.signal })
  .catch(err => {
    if (err.name === 'AbortError') {
      console.log('Fetch aborted');
    }
  });
// 取消请求
controller.abort();

JavaScript

总结增强

JavaScript事件循环机制的核心差异体现在:

  1. 架构层面:浏览器围绕UI渲染优化,Node.js侧重I/O吞吐
  1. 任务调度:Node.js的process.nextTick插入阶段切换点,浏览器微任务在宏任务之后立即执行
  1. API生态:浏览器包含DOM相关API,Node.js提供系统级I/O能力

理解这些差异有助于:

  • 在浏览器中实现流畅的UI响应
  • 在Node.js中构建高吞吐服务
  • 避免跨环境代码的行为不一致
  • 合理利用各环境的独有特性进行优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值