理解 setImmediate()

当你想异步执行某段代码,但又希望尽快执行时,一个选择是使用 Node.js 提供的 setImmediate() 函数。

(() => {
  // run something
});

任何作为 setImmediate() 参数传递的函数都是一个回调,它会在事件循环的下一次迭代中执行。

setImmediate()setTimeout(() => {}, 0)(传递 0 毫秒超时)、process.nextTick()Promise.then() 有何不同?

传递给 process.nextTick() 的函数将在事件循环的当前迭代中,在当前操作结束后执行。这意味着它将总是在 setTimeoutsetImmediate 之前执行。

一个延迟为 0 毫秒的 setTimeout() 回调与 setImmediate() 非常相似。执行顺序将取决于多种因素,但它们都将在事件循环的下一次迭代中运行。

process.nextTick 回调被添加到 process.nextTick 队列中。Promise.then() 回调被添加到 promises 微任务队列中。setTimeoutsetImmediate 回调被添加到 宏任务队列中。

事件循环首先执行 process.nextTick 队列中的任务,然后执行 promises 微任务队列中的任务,最后执行 宏任务队列中的任务。

这里有一个例子来展示 setImmediate()process.nextTick()Promise.then() 之间的顺序。

const  = () => .('baz');
const  = () => .('foo');
const  = () => .('zoo');

const  = () => {
  .('start');
  ();
  new ((, ) => {
    ('bar');
  }).( => {
    .();
    .();
  });
  .();
};

();

// start foo bar zoo baz

这段代码将首先调用 start(),然后在 process.nextTick 队列中调用 foo()。之后,它将处理 promises 微任务队列,这会打印出 bar 并同时将 zoo() 添加到 process.nextTick 队列中。然后它会调用刚刚添加的 zoo()。最后,调用 宏任务队列中的 baz()

上述原则在 CommonJS 情况下成立,但请记住,在 ES 模块中(例如 mjs 文件),执行顺序会有所不同。

// start bar foo zoo baz

这是因为正在加载的 ES 模块被包装成一个异步操作,因此整个脚本实际上已经位于 promises 微任务队列中。所以当 promise 立即解决时,它的回调被追加到 微任务 队列。Node.js 会尝试清空该队列,直到移动到任何其他队列,因此你会看到它首先输出 bar

阅读时间
2 分钟
作者
贡献
编辑此页面