当 microtaskMode 为 'afterEvaluate' 时,注意不要在不同上下文之间共享 Promise
【When microtaskMode is 'afterEvaluate', beware sharing Promises between Contexts】
在 'afterEvaluate' 模式下,Context 拥有自己的微任务队列,独立于外部(主)上下文使用的全局微任务队列。虽然这种模式对于在异步任务中强制 timeout 和启用 breakOnSigint 是必要的,但它也使跨上下文共享 promise 变得具有挑战性。
【In 'afterEvaluate' mode, the Context has its own microtask queue, separate
from the global microtask queue used by the outer (main) context. While this
mode is necessary to enforce timeout and enable breakOnSigint with
asynchronous tasks, it also makes sharing promises between contexts challenging.】
在下面的示例中,一个 promise 在内部上下文中创建,并与外部上下文共享。当外部上下文对该 promise 使用 await 时,外部上下文的执行流程以一种令人意外的方式被打断:日志语句从未被执行。
【In the example below, a promise is created in the inner context and shared with
the outer context. When the outer context await on the promise, the execution
flow of the outer context is disrupted in a surprising way: the log statement
is never executed.】
import * as vm from 'node:vm';
const inner_context = vm.createContext({}, { microtaskMode: 'afterEvaluate' });
// runInContext() returns a Promise created in the inner context.
const inner_promise = vm.runInContext(
'Promise.resolve()',
context,
);
// As part of performing `await`, the JavaScript runtime must enqueue a task
// on the microtask queue of the context where `inner_promise` was created.
// A task is added on the inner microtask queue, but **it will not be run
// automatically**: this task will remain pending indefinitely.
//
// Since the outer microtask queue is empty, execution in the outer module
// falls through, and the log statement below is never executed.
await inner_promise;
console.log('this will NOT be printed'); 要在具有不同微任务队列的上下文之间成功共享 Promise,必须确保每当外部上下文在内部微任务队列中入队一个任务时,内部微任务队列的任务都会被执行。
【To successfully share promises between contexts with different microtask queues, it is necessary to ensure that tasks on the inner microtask queue will be run whenever the outer context enqueues a task on the inner microtask queue.】
在给定上下文的微任务队列中的任务,会在对使用该上下文的脚本或模块调用 runInContext() 或 SourceTextModule.evaluate() 时运行。在我们的示例中,可以通过在 await inner_promise 之前安排第二次调用 runInContext() 来恢复正常的执行流程。
【The tasks on the microtask queue of a given context are run whenever
runInContext() or SourceTextModule.evaluate() are invoked on a script or
module using this context. In our example, the normal execution flow can be
restored by scheduling a second call to runInContext() before await inner_promise.】
// Schedule `runInContext()` to manually drain the inner context microtask
// queue; it will run after the `await` statement below.
setImmediate(() => {
vm.runInContext('', context);
});
await inner_promise;
console.log('OK'); **注意:**严格来说,在此模式下,node:vm 与 ECMAScript 对 排队作业 的规范字面意义有所出入,它允许来自不同上下文的异步任务以与入队顺序不同的顺序执行。