何时使用 queueMicrotask() 与 process.nextTick()


【When to use queueMicrotask() vs. process.nextTick()

queueMicrotask() API 是 process.nextTick() 的一种替代方案,它不是使用“下一次滴答队列”,而是使用与执行已解决的 Promise 的 then、catch 和 finally 处理程序相同的微任务队列来延迟函数的执行。

【The queueMicrotask() API is an alternative to process.nextTick() that instead of using the "next tick queue" defers execution of a function using the same microtask queue used to execute the then, catch, and finally handlers of resolved promises.】

在 Node.js 中,每次“下一个 tick 队列”被清空时,微任务队列会紧接着被清空。

【Within Node.js, every time the "next tick queue" is drained, the microtask queue is drained immediately after.】

所以在 CJS 模块中,process.nextTick() 的回调总是在 queueMicrotask() 的回调之前执行。然而,由于 ESM 模块已经作为微任务队列的一部分进行处理,所以在那里 queueMicrotask() 的回调总是在 process.nextTick() 的回调之前执行,因为 Node.js 已经在处理清空微任务队列的过程。

【So in CJS modules process.nextTick() callbacks are always run before queueMicrotask() ones. However since ESM modules are processed already as part of the microtask queue, there queueMicrotask() callbacks are always executed before process.nextTick() ones since Node.js is already in the process of draining the microtask queue.】

import { nextTick } from 'node:process';

Promise.resolve().then(() => console.log('resolve'));
queueMicrotask(() => console.log('microtask'));
nextTick(() => console.log('nextTick'));
// Output:
// resolve
// microtask
// nextTickconst { nextTick } = require('node:process');

Promise.resolve().then(() => console.log('resolve'));
queueMicrotask(() => console.log('microtask'));
nextTick(() => console.log('nextTick'));
// Output:
// nextTick
// resolve
// microtask

对于大多数用户态用例,queueMicrotask() API 提供了一种可移植且可靠的延迟执行机制,可以在多个 JavaScript 平台环境中使用,并且应优先于 process.nextTick()。在简单的场景中,queueMicrotask() 可以作为 process.nextTick() 的直接替代方案。

【For most userland use cases, the queueMicrotask() API provides a portable and reliable mechanism for deferring execution that works across multiple JavaScript platform environments and should be favored over process.nextTick(). In simple scenarios, queueMicrotask() can be a drop-in replacement for process.nextTick().】

console.log('start');
queueMicrotask(() => {
  console.log('microtask callback');
});
console.log('scheduled');
// Output:
// start
// scheduled
// microtask callback 

两个 API 之间一个值得注意的区别是,process.nextTick() 允许指定额外的值,这些值将在延迟函数被调用时作为参数传入。要使用 queueMicrotask() 实现相同的效果,则需要使用闭包或绑定函数:

【One note-worthy difference between the two APIs is that process.nextTick() allows specifying additional values that will be passed as arguments to the deferred function when it is called. Achieving the same result with queueMicrotask() requires using either a closure or a bound function:】

function deferred(a, b) {
  console.log('microtask', a + b);
}

console.log('start');
queueMicrotask(deferred.bind(undefined, 1, 2));
console.log('scheduled');
// Output:
// start
// scheduled
// microtask 3 

在处理从下一次事件循环队列和微任务队列中抛出的错误时存在一些细微差别。应尽可能在排队的微任务回调中处理抛出的错误。如果不能处理,可以使用 process.on('uncaughtException') 事件处理程序来捕获和处理这些错误。

【There are minor differences in the way errors raised from within the next tick queue and microtask queue are handled. Errors thrown within a queued microtask callback should be handled within the queued callback when possible. If they are not, the process.on('uncaughtException') event handler can be used to capture and handle the errors.】

遇到疑问时,除非需要 process.nextTick() 的特定功能,否则应使用 queueMicrotask()

【When in doubt, unless the specific capabilities of process.nextTick() are needed, use queueMicrotask().】