理解 setImmediate()
【Understanding setImmediate()】
当你想异步执行某段代码,但又希望尽快执行时,一个选项是使用 Node.js 提供的 setImmediate() 函数:
【When you want to execute some piece of code asynchronously, but as soon as possible, one option is to use the setImmediate() function provided by Node.js:】
(() => {
// run something
});
作为 setImmediate() 参数传递的任何函数都是在事件循环的下一次迭代中执行的回调。
【Any function passed as the setImmediate() argument is a callback that's executed in the next iteration of the event loop.】
setImmediate() 与 setTimeout(() => {}, 0)(传入 0 毫秒延迟)、process.nextTick() 和 Promise.then() 有何不同?
【How is setImmediate() different from setTimeout(() => {}, 0) (passing a 0ms timeout), and from process.nextTick() and Promise.then()?】
传递给 process.nextTick() 的函数将在事件循环的当前迭代中执行,在当前操作结束后。这意味着它总是会在 setTimeout 和 setImmediate 之前执行。
【A function passed to process.nextTick() is going to be executed on the current iteration of the event loop, after the current operation ends. This means it will always execute before setTimeout and setImmediate.】
setTimeout() 的回调函数如果延迟为 0 毫秒,与 setImmediate() 非常相似。执行顺序将取决于各种因素,但它们都会在事件循环的下一次迭代中运行。
【A setTimeout() callback with a 0ms delay is very similar to setImmediate(). The execution order will depend on various factors, but they will be both run in the next iteration of the event loop.】
一个 process.nextTick 回调被添加到 process.nextTick queue。一个 Promise.then() 回调被添加到 promises microtask queue。一个 setTimeout、setImmediate 回调被添加到 macrotask queue。
【A process.nextTick callback is added to process.nextTick queue. A Promise.then() callback is added to promises microtask queue. A setTimeout, setImmediate callback is added to macrotask queue.】
事件循环首先执行 process.nextTick 队列中的任务,然后执行 promises 微任务队列,最后执行宏任务队列。
【Event loop executes tasks in process.nextTick queue first, and then executes promises microtask queue, and then executes macrotask queue.】
这里有一个示例,展示 setImmediate()、process.nextTick() 和 Promise.then() 之间的执行顺序:
【Here is an example to show the order between setImmediate(), process.nextTick() and Promise.then():】
const = () => .('baz');
const = () => .('foo');
const = () => .('zoo');
const = () => {
.('start');
();
new ((, ) => {
('bar');
}).( => {
.();
.();
});
.();
};
();
// start foo bar zoo baz
这段代码将首先调用 start(),然后在 process.nextTick 队列 中调用 foo()。之后,它将处理 promises 微任务队列,这会打印 bar 并同时在 process.nextTick 队列 中添加 zoo()。然后,它会调用刚刚添加的 zoo()。最后,macrotask 队列 中的 baz() 被调用。
【This code will first call start(), then call foo() in process.nextTick queue. After that, it will handle promises microtask queue, which prints bar and adds zoo() in process.nextTick queue at the same time. Then it will call zoo() which has just been added. In the end, the baz() in macrotask queue is called.】
上述原则在 CommonJS 情况下成立,但请记住,在 ES 模块(例如 mjs 文件)中,执行顺序会有所不同:
【The principle aforementioned holds true in CommonJS cases, but keep in mind in ES Modules, e.g. mjs files, the execution order will be different:】
// start bar foo zoo baz
这是因为正在加载的 ES 模块被封装为异步操作,因此整个脚本实际上已经在 promises microtask queue 中。所以当 promise 立即被解析时,它的回调会被加入到 microtask 队列中。Node.js 会尝试清理该队列,直到转到其他队列,因此你会看到它首先输出 bar。
【This is because the ES Module being loaded is wrapped as an asynchronous operation, and thus the entire script is actually already in the promises microtask queue. So when the promise is immediately resolved, its callback is appended to the microtask queue. Node.js will attempt to clear the queue until moving to any other queue, and hence you will see it outputs bar first.】