微任务与宏任务

  • 时间:2025-11-11 20:48 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:在 Node.js 中,微任务(Microtasks)和宏任务(Macrotasks)是事件循环(Event Loop)的核心组成部分,用于管理异步操作的执行顺序。这两种任务机制源于 JavaScript 的单线程模型,通过 libuv 库的底层支持,实现非阻塞 I/O 和高效任务调度。微任务和宏任务的区分允许开发人员准确控制异步代码的优先级,避免事件循环饥饿,并确保关键操作(如 Promise

在 Node.js 中,微任务(Microtasks)和宏任务(Macrotasks)是事件循环(Event Loop)的核心组成部分,用于管理异步操作的执行顺序。这两种任务机制源于 JavaScript 的单线程模型,通过 libuv 库的底层支持,实现非阻塞 I/O 和高效任务调度。微任务和宏任务的区分允许开发人员准确控制异步代码的优先级,避免事件循环饥饿,并确保关键操作(如 Promise 解析)在宏任务间及时执行。这在处理高并发、网络请求或实时计算时特别有用。

基本概念

Node.js 的事件循环基于 libuv 库,实现了一个阶段化的循环模型,用于处理异步任务。事件循环不是 JavaScript V8 引擎的一部分,而是 Node.js 运行时通过 C++ 实现的。循环分为多个阶段(如 timers、poll、check),每个阶段处理特定类型的宏任务。

  • 宏任务(Macrotasks) :对应事件循环的“主要任务”,在特定阶段执行,一般涉及 I/O 或定时器。宏任务队列由 libuv 管理。 #技术分享
  • 微任务(Microtasks) :更高优先级的“子任务”,在每个宏任务后或事件循环阶段间执行。微任务队列由 V8 引擎和 Node.js 共同管理。

事件循环的执行顺序:同步代码 → 微任务队列 → 宏任务阶段(循环)。如果微任务不断添加新微任务,可能导致宏任务“饥饿”。

Node.js 提供了两个微任务队列:Next Tick Queue(process.nextTick)和 Microtask Queue(Promise callbacks)。

微任务:高优先级异步执行

微任务用于在当前宏任务结束后立即执行异步代码,确保优先级高于宏任务。微任务队列在每个事件循环阶段后清空。

基本用法

使用 process.nextTick 或 Promise:

console.log('Start');

process.nextTick(() => { console.log('Next Tick'); });

Promise.resolve().then(() => { console.log('Promise Then'); });

console.log('End');

这里,同步代码先执行,然后是 Next Tick Queue,最后是 Microtask Queue。

自定义微任务

微任务一般通过内置 API 实现,但你可以链式使用:

process.nextTick(() => {
  console.log('Tick 1');
  process.nextTick(() => console.log('Tick 2'));
});

Promise.resolve().then(() => { console.log('Then 1'); Promise.resolve().then(() => console.log('Then 2')); });

Next Tick 优先于 Promise then 执行,由于 Next Tick Queue 在 Microtask Queue 前清空。

微任务的关键特性:递归执行直到队列为空,避免阻塞事件循环。

宏任务:事件循环阶段任务

宏任务对应 libuv 的事件循环阶段,如定时器回调或 I/O 完成。

基本用法

使用 setTimeout 、setImmediate 或 I/O:

setTimeout(() => console.log('Timeout'), 0);

setImmediate(() => console.log('Immediate'));

fs.readFile('file.txt', (err, data) => { console.log('File Read'); });

这些在 timers、check 或 poll 阶段执行。

自定义宏任务

宏任务一般由 libuv API 调度,如自定义定时器或 socket 事件。

宏任务的关键:阶段化执行,确保 I/O 非阻塞。

区别:优先级与执行顺序

  • 定义
  • 微任务:小粒度、高优先级任务,如 process.nextTick、Promise.then、async/await、MutationObserver(浏览器侧)。
  • 宏任务:大粒度任务,如 setTimeout、setInterval、setImmediate、I/O callbacks、UI 渲染(浏览器侧)。
  • 区别
  • 优先级 :微任务在每个宏任务后执行,所有微任务清空后才进入下一个宏任务。
  • 队列 :微任务有两个队列(Next Tick > Microtask);宏任务分散在 libuv 阶段队列中。
  • 执行时机 :微任务在 JS 栈清空时执行,可能在宏任务内多次运行;宏任务在事件循环特定阶段。
  • 潜在问题 :无限微任务可能导致宏任务饥饿(如 I/O 延迟)。
  • 执行顺序
  • 执行同步代码。
  • 清空 Next Tick Queue。
  • 清空 Microtask Queue。
  • 进入下一个宏任务阶段(e.g., timers)。

5.

示例:

console.log('Sync 1');

setTimeout(() => console.log('Timeout'), 0);

Promise.resolve().then(() => console.log('Promise'));

process.nextTick(() => console.log('Next Tick'));

console.log('Sync 2');

使用场景

  • 微任务场景
  • 立即异步:process.nextTick 用于在当前栈后执行,避免阻塞(如递归 setTimeout 的替代)。
  • Promise 链:确保 then/catch 在 resolve 后立即执行,支持 async/await。
  • 错误处理:捕获微任务中的错误而不中断宏任务。
  • 示例:数据库事务后立即更新缓存。
  • 宏任务场景
  • 定时任务:setTimeout 用于延迟执行。
  • I/O 操作:文件读写、网络请求。
  • 批处理:setImmediate 用于在 poll 阶段后执行,避免阻塞 I/O。
  • 示例:UI 更新后渲染(浏览器),或服务器响应后日志。

为什么要有这种区分?微任务允许细粒度控制,确保关键异步逻辑(如状态更新)优先;宏任务防止单线程阻塞,确保 I/O 公平调度。区分避免了所有任务同级导致的混乱,提高了异步代码的可预测性。

深入原理:libuv 底层原理

Node.js 的事件循环由 libuv 提供 C++ 实现,Node.js 在其上添加微任务支持。libuv 的 uv_run() 函数驱动循环,Node.js 通过 V8 集成微任务队列。以下通过 Node.js 源码展示原理。

libuv 事件循环核心

libuv 的事件循环在 uv_run(uv_loop_t* loop, uv_run_mode mode) 中实现(libuv/src/unix/core.c 或 win/core.c):

int uv_run(uv_loop_t *loop, uv_run_mode mode) {
  while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    uv__run_timers(loop);
    r = uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);
    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);
  }
  return r;
}

libuv 处理宏任务阶段:timers (setTimeout)、poll (I/O)、check (setImmediate) 等。Node.js 包装此循环,插入微任务执行。

JavaScript Node.js 集成微任务

在 Node.js src/node.cc 中,Run() 函数包装 uv_run,并运行微任务:

int NodeMainInstance::Run() {
  Isolate* isolate = env_->isolate();
  while (true) {
    bool platform_finished = false;
    uv_run(env_->event_loop(), UV_RUN_ONCE);

platform_finished = !v8_platform->PumpMessageLoop(isolate_data_, isolate);

env_->RunAndClearNativeImmediates(); if (EmitProcessBeforeExit(env_)) continue;

v8::MicrotasksPolicy policy = v8::MicrotasksPolicy::kExplicit; isolate->RunMicrotasks();

if (env_->TickInfo()->HasScheduledTasks()) { env_->RunAndClearNextTicks(); }

if (platform_finished && !env_->TickInfo()->HasScheduledTasks()) break; } return exit_code_; }
  • uv_run(UV_RUN_ONCE):执行一个宏任务阶段。
  • isolate->RunMicrotasks():调用 V8 的微任务队列(Promise callbacks)。
  • env_->RunAndClearNextTicks():处理 Node.js 特有的 Next Tick Queue(process.nextTick)。

Next Tick Queue 优先于 V8 Microtask Queue,由于它在 RunMicrotasks 前或后检查。

C Next Tick Queue 源码


lib/internal/process/task_queues.js 中,实现 nextTick Queue:

const { nextTickQueue } = internalBinding('task_queue');

function processTicksAndRejections() { let tock; do { while (tock = nextTickQueue.shift()) { const asyncId = tock[async_id_symbol]; emitBefore(asyncId, tock[trigger_async_id_symbol]); try { const callback = tock.callback; if (tock.args === undefined) { callback(); } else { Reflect.apply(callback, undefined, tock.args); } } finally { emitAfter(asyncId); } } runMicrotaskQueue(); } while (!nextTickQueue.isEmpty() || hasMicrotasks()); }
  • nextTickQueue:一个数组,push nextTick 回调。
  • processTicksAndRejections():递归清空队列,先 Next Tick,后 Microtasks (runMicrotaskQueue 调用 V8 RunMicrotasks)。


lib/internal/process/next_tick.js:

function nextTick(callback) {

  if (typeof callback !== 'function') throw new TypeError('callback is not a function');

  nextTickQueue.push(callback);

  if (!ticking) {
    setTickScheduled(true);
    scheduleMicrotask();
  }
}

为什么区分?

从源码可见,微任务(Next Tick + Microtasks)在每个 uv_run 迭代后执行,确保高优先级任务(如 Promise 解析)不被宏任务(如长 I/O)延迟。宏任务阶段化(libuv)防止单线程死锁。区分允许:

  • 优先微任务:支持 async/await 的“同步”语义。
  • 避免饥饿:宏任务确保 I/O 进展。
  • 性能:微任务递归清空,宏任务分阶段。

如果无区分,所有任务同队列,可能导致优先级混乱或循环阻塞。

总结

微任务和宏任务是 Node.js 异步模型的基石,从简单用法到自定义调度,由浅入深地提升代码控制力。通过事件循环的阶段化和队列管理,我们可以构建高效应用。底层 libuv 与 V8 的集成,通过 uv_run 和 RunMicrotasks,确保非阻塞。

  • 全部评论(0)
最新发布的资讯信息
【系统环境|】最低 2 美元,这 55 款 macOS & Windows 应用一次全都入手(2025-11-11 22:01)
【系统环境|】SCI期刊对论文图片有哪些要求?(2025-11-11 22:00)
【系统环境|】论文缩写大全,拿走不谢(2025-11-11 22:00)
【系统环境|】阿甘正传高频词整理 GRE托福四六级词汇整理(2025-11-11 21:59)
【系统环境|】矢量图形编辑应用程序-WinFIG(2025-11-11 21:59)
【系统环境|】Figma上市首日暴涨250%的深层逻辑:为什么AI时代协作平台更加不可替代?(2025-11-11 21:58)
【系统环境|】FigJam是什么?一文读懂在线白板软件的方方面面!(2025-11-11 21:58)
【系统环境|】在windows上有什么好用的书写白板软件?(2025-11-11 21:57)
【系统环境|】Docker基础应用之nginx(2025-11-11 21:57)
【系统环境|】VS Code 新手必装插件清单(2025-11-11 21:56)
手机二维码手机访问领取大礼包
返回顶部