与异步任务和 Promises 的超时交互
🌐 Timeout interactions with asynchronous tasks and Promises
Promise 和 async function 可以让 JavaScript 引擎异步调度任务。默认情况下,这些任务会在当前调用栈上的所有 JavaScript 函数执行完毕后运行。这使得可以绕过 timeout 和 breakOnSigint 选项的功能。
例如,以下由 vm.runInNewContext() 执行的代码,在 5 毫秒的超时时间内,会在一个 promise 解决后安排一个无限循环运行。这个被安排的循环永远不会被超时中断:
🌐 For example, the following code executed by vm.runInNewContext() with a
timeout of 5 milliseconds schedules an infinite loop to run after a promise
resolves. The scheduled loop is never interrupted by the timeout:
const vm = require('node:vm');
function loop() {
console.log('entering loop');
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5 },
);
// This is printed *before* 'entering loop' (!)
console.log('done executing'); 可以通过向创建 Context 的代码传递 microtaskMode: 'afterEvaluate' 来解决这个问题:
🌐 This can be addressed by passing microtaskMode: 'afterEvaluate' to the code
that creates the Context:
const vm = require('node:vm');
function loop() {
while (1) console.log(Date.now());
}
vm.runInNewContext(
'Promise.resolve().then(() => loop());',
{ loop, console },
{ timeout: 5, microtaskMode: 'afterEvaluate' },
); 在这种情况下,通过 promise.then() 调度的微任务将在从 vm.runInNewContext() 返回之前运行,并会被 timeout 功能中断。这仅适用于在 vm.Context 中运行的代码,例如 vm.runInThisContext() 不支持此选项。
🌐 In this case, the microtask scheduled through promise.then() will be run
before returning from vm.runInNewContext(), and will be interrupted
by the timeout functionality. This applies only to code running in a
vm.Context, so e.g. vm.runInThisContext() does not take this option.
Promise 回调会进入它们被创建的上下文的微任务队列。例如,如果在上述示例中将 () => loop() 替换为仅 loop,那么 loop 将被推入全局微任务队列,因为它是来自外部(主)上下文的函数,因此也能够逃脱超时。
🌐 Promise callbacks are entered into the microtask queue of the context in which
they were created. For example, if () => loop() is replaced with just loop
in the above example, then loop will be pushed into the global microtask
queue, because it is a function from the outer (main) context, and thus will
also be able to escape the timeout.
如果在 vm.Context 中提供异步调度函数,例如 process.nextTick()、queueMicrotask()、setTimeout()、setImmediate() 等,传递给它们的函数将被添加到全局队列中,而这些队列是所有上下文共享的。因此,传递给这些函数的回调也无法通过超时来控制。
🌐 If asynchronous scheduling functions such as process.nextTick(),
queueMicrotask(), setTimeout(), setImmediate(), etc. are made available
inside a vm.Context, functions passed to them will be added to global queues,
which are shared by all contexts. Therefore, callbacks passed to those functions
are not controllable through the timeout either.