与异步任务和 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.】