与异步任务和 Promises 的超时交互
¥Timeout interactions with asynchronous tasks and Promises
Promise
和 async function
可以异步调度 JavaScript 引擎运行的任务。默认情况下,这些任务在当前堆栈上的所有 JavaScript 函数执行完毕后运行。这允许转义 timeout
和 breakOnSigint
选项的功能。
¥Promise
s and async function
s can schedule tasks run by the JavaScript
engine asynchronously. By default, these tasks are run after all JavaScript
functions on the current stack are done executing.
This allows escaping the functionality of the timeout
and
breakOnSigint
options.
例如,以下代码由 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');
这可以通过将 microtaskMode: 'afterEvaluate'
传给创建 Context
的代码来解决:
¥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.
如果 process.nextTick()
、queueMicrotask()
、setTimeout()
、setImmediate()
等异步调度函数在 vm.Context
中可用,传递给它们的函数将被添加到全局队列中,全局队列由所有上下文共享。因此,传给这些函数的回调也无法通过超时控制。
¥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.