@@ -188,6 +188,9 @@ overhead.
188188<!-- YAML
189189added: v0.3.1
190190changes:
191+ - version: REPLACEME
192+ pr-url: https://github.com/nodejs/node/pull/34023
193+ description: The `microtaskMode` option is supported now.
191194 - version: v10.0.0
192195 pr-url: https://github.com/nodejs/node/pull/19016
193196 description: The `contextCodeGeneration` option is supported now.
@@ -225,6 +228,10 @@ changes:
225228 ` EvalError ` . ** Default:** ` true ` .
226229 * ` wasm ` {boolean} If set to false any attempt to compile a WebAssembly
227230 module will throw a ` WebAssembly.CompileError ` . ** Default:** ` true ` .
231+ * ` microtaskMode ` {string} If set to ` afterEvaluate ` , microtasks (tasks
232+ scheduled through ` Promise ` s any ` async function ` s) will be run immediately
233+ after the script has run. They are included in the ` timeout ` and
234+ ` breakOnSigint ` scopes in that case.
228235* Returns: {any} the result of the very last statement executed in the script.
229236
230237First contextifies the given ` contextObject ` , runs the compiled code contained
@@ -842,6 +849,9 @@ function with the given `params`.
842849<!-- YAML
843850added: v0.3.1
844851changes:
852+ - version: REPLACEME
853+ pr-url: https:// github.com/nodejs/node/pull/34023
854+ description: The `microtaskMode` option is supported now.
845855 - version: v10.0.0
846856 pr-url: https:// github.com/nodejs/node/pull/19398
847857 description: The first argument can no longer be a function.
@@ -867,6 +877,10 @@ changes:
867877 `EvalError`. **Default:** `true`.
868878 * `wasm` {boolean} If set to false any attempt to compile a WebAssembly
869879 module will throw a ` WebAssembly.CompileError` . ** Default: ** ` true` .
880+ * ` microtaskMode` {string} If set to ` afterEvaluate` , microtasks (tasks
881+ scheduled through ` Promise` s any ` async function` s) will be run immediately
882+ after a script has run through [` script.runInContext()` ][].
883+ They are included in the ` timeout` and ` breakOnSigint` scopes in that case.
870884* Returns: {Object } contextified object.
871885
872886If given a ` contextObject` , the ` vm.createContext()` method will [prepare
@@ -998,6 +1012,9 @@ console.log(contextObject);
9981012<!-- YAML
9991013added: v0.3.1
10001014changes:
1015+ - version: REPLACEME
1016+ pr-url: https://github.com/nodejs/node/pull/34023
1017+ description: The `microtaskMode` option is supported now.
10011018 - version: v10.0.0
10021019 pr-url: https://github.com/nodejs/node/pull/19016
10031020 description: The `contextCodeGeneration` option is supported now.
@@ -1064,6 +1081,10 @@ changes:
10641081 * Returns: {Module Namespace Object|vm.Module} Returning a `vm.Module` is
10651082 recommended in order to take advantage of error tracking, and to avoid
10661083 issues with namespaces that contain `then` function exports.
1084+ * `microtaskMode` {string} If set to `afterEvaluate`, microtasks (tasks
1085+ scheduled through `Promise`s any `async function`s) will be run immediately
1086+ after the script has run. They are included in the `timeout` and
1087+ `breakOnSigint` scopes in that case.
10671088* Returns: {any} the result of the very last statement executed in the script.
10681089
10691090The `vm.runInNewContext()` first contextifies the given `contextObject` (or
@@ -1220,13 +1241,13 @@ within which it can operate. The process of creating the V8 Context and
12201241associating it with the ` contextObject` is what this document refers to as
12211242" contextifying" the object.
12221243
1223- ## Timeout limitations when using ` process.nextTick() ` , promises, and ` queueMicrotask() `
1244+ ## Timeout interactions with asynchronous tasks and Promises
12241245
1225- Because of the internal mechanics of how the ` process.nextTick() ` queue and
1226- the microtask queue that underlies Promises are implemented within V8 and
1227- Node . js , it is possible for code running within a context to " escape " the
1228- ` timeout ` set using ` vm.runInContext() ` , ` vm.runInNewContext() ` , and
1229- ` vm.runInThisContext() ` .
1246+ ` Promise ` s and ` async function ` s can schedule tasks run by the JavaScript
1247+ engine asynchronously . By default, these tasks are run after all JavaScript
1248+ functions on the current stack are done executing.
1249+ This allows escaping the functionality of the ` timeout ` and
1250+ ` breakOnSigint ` options .
12301251
12311252For example, the following code executed by ` vm.runInNewContext()` with a
12321253timeout of 5 milliseconds schedules an infinite loop to run after a promise
@@ -1236,21 +1257,52 @@ resolves. The scheduled loop is never interrupted by the timeout:
12361257const vm = require('vm');
12371258
12381259function loop() {
1260+ console.log('entering loop');
12391261 while (1) console.log(Date.now());
12401262}
12411263
12421264vm.runInNewContext(
1243- 'Promise.resolve().then(loop);',
1265+ 'Promise.resolve().then(() => loop() );',
12441266 { loop, console },
12451267 { timeout: 5 }
12461268);
1269+ // This prints *before* 'entering loop' (!)
1270+ console.log('done executing');
12471271` ` `
12481272
1249- This issue also occurs when the ` loop() ` call is scheduled using
1250- the ` process.nextTick() ` and ` queueMicrotask() ` functions.
1273+ This can be addressed by passing ` microtaskMode: 'afterEvaluate' ` to the code
1274+ that creates the ` Context ` :
12511275
1252- This issue occurs because all contexts share the same microtask and nextTick
1253- queues.
1276+ ` ` ` js
1277+ const vm = require('vm');
1278+
1279+ function loop() {
1280+ while (1) console.log(Date.now());
1281+ }
1282+
1283+ vm.runInNewContext(
1284+ 'Promise.resolve().then(() => loop());',
1285+ { loop, console },
1286+ { timeout: 5, microtaskMode: 'afterEvaluate' }
1287+ );
1288+ ` ` `
1289+
1290+ In this case, the microtask scheduled through ` promise.then()` will be run
1291+ before returning from ` vm.runInNewContext()` , and will be interrupted
1292+ by the ` timeout` functionality . This applies only to code running in a
1293+ ` vm.Context` , so e .g . [` vm.runInThisContext()` ][] does not take this option.
1294+
1295+ Promise callbacks are entered into the microtask queue of the context in which
1296+ they were created . For example, if ` () => loop()` is replaced with just ` loop`
1297+ in the above example, then ` loop` will be pushed into the global microtask
1298+ queue, because it is a function from the outer (main ) context, and thus will
1299+ also be able to escape the timeout.
1300+
1301+ If asynchronous scheduling functions such as `process.nextTick()`,
1302+ `queueMicrotask()`, `setTimeout()`, `setImmediate()`, etc. are made available
1303+ inside a `vm.Context`, functions passed to them will be added to global queues,
1304+ which are shared by all contexts. Therefore, callbacks passed to those functions
1305+ are not controllable through the timeout either.
12541306
12551307[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`]: errors.html#ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING
12561308[`ERR_VM_MODULE_STATUS`]: errors.html#ERR_VM_MODULE_STATUS
0 commit comments