@@ -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
@@ -846,6 +853,9 @@ function with the given `params`.
846853<!-- YAML
847854added: v0.3.1
848855changes:
856+ - version: REPLACEME
857+ pr-url: https:// github.com/nodejs/node/pull/34023
858+ description: The `microtaskMode` option is supported now.
849859 - version: v10.0.0
850860 pr-url: https:// github.com/nodejs/node/pull/19398
851861 description: The first argument can no longer be a function.
@@ -871,6 +881,10 @@ changes:
871881 `EvalError`. **Default:** `true`.
872882 * `wasm` {boolean} If set to false any attempt to compile a WebAssembly
873883 module will throw a ` WebAssembly.CompileError` . ** Default: ** ` true` .
884+ * ` microtaskMode` {string} If set to ` afterEvaluate` , microtasks (tasks
885+ scheduled through ` Promise` s any ` async function` s) will be run immediately
886+ after a script has run through [` script.runInContext()` ][].
887+ They are included in the ` timeout` and ` breakOnSigint` scopes in that case.
874888* Returns: {Object } contextified object.
875889
876890If given a ` contextObject` , the ` vm.createContext()` method will [prepare
@@ -1002,6 +1016,9 @@ console.log(contextObject);
10021016<!-- YAML
10031017added: v0.3.1
10041018changes:
1019+ - version: REPLACEME
1020+ pr-url: https://github.com/nodejs/node/pull/34023
1021+ description: The `microtaskMode` option is supported now.
10051022 - version: v10.0.0
10061023 pr-url: https://github.com/nodejs/node/pull/19016
10071024 description: The `contextCodeGeneration` option is supported now.
@@ -1068,6 +1085,10 @@ changes:
10681085 * Returns: {Module Namespace Object|vm.Module} Returning a `vm.Module` is
10691086 recommended in order to take advantage of error tracking, and to avoid
10701087 issues with namespaces that contain `then` function exports.
1088+ * `microtaskMode` {string} If set to `afterEvaluate`, microtasks (tasks
1089+ scheduled through `Promise`s any `async function`s) will be run immediately
1090+ after the script has run. They are included in the `timeout` and
1091+ `breakOnSigint` scopes in that case.
10711092* Returns: {any} the result of the very last statement executed in the script.
10721093
10731094The `vm.runInNewContext()` first contextifies the given `contextObject` (or
@@ -1224,13 +1245,13 @@ within which it can operate. The process of creating the V8 Context and
12241245associating it with the ` contextObject` is what this document refers to as
12251246" contextifying" the object.
12261247
1227- ## Timeout limitations when using ` process.nextTick() ` , promises, and ` queueMicrotask() `
1248+ ## Timeout interactions with asynchronous tasks and Promises
12281249
1229- Because of the internal mechanics of how the ` process.nextTick() ` queue and
1230- the microtask queue that underlies Promises are implemented within V8 and
1231- Node . js , it is possible for code running within a context to " escape " the
1232- ` timeout ` set using ` vm.runInContext() ` , ` vm.runInNewContext() ` , and
1233- ` vm.runInThisContext() ` .
1250+ ` Promise ` s and ` async function ` s can schedule tasks run by the JavaScript
1251+ engine asynchronously . By default, these tasks are run after all JavaScript
1252+ functions on the current stack are done executing.
1253+ This allows escaping the functionality of the ` timeout ` and
1254+ ` breakOnSigint ` options .
12341255
12351256For example, the following code executed by ` vm.runInNewContext()` with a
12361257timeout of 5 milliseconds schedules an infinite loop to run after a promise
@@ -1240,21 +1261,52 @@ resolves. The scheduled loop is never interrupted by the timeout:
12401261const vm = require('vm');
12411262
12421263function loop() {
1264+ console.log('entering loop');
12431265 while (1) console.log(Date.now());
12441266}
12451267
12461268vm.runInNewContext(
1247- 'Promise.resolve().then(loop);',
1269+ 'Promise.resolve().then(() => loop() );',
12481270 { loop, console },
12491271 { timeout: 5 }
12501272);
1273+ // This prints *before* 'entering loop' (!)
1274+ console.log('done executing');
12511275` ` `
12521276
1253- This issue also occurs when the ` loop() ` call is scheduled using
1254- the ` process.nextTick() ` and ` queueMicrotask() ` functions.
1277+ This can be addressed by passing ` microtaskMode: 'afterEvaluate' ` to the code
1278+ that creates the ` Context ` :
12551279
1256- This issue occurs because all contexts share the same microtask and nextTick
1257- queues.
1280+ ` ` ` js
1281+ const vm = require('vm');
1282+
1283+ function loop() {
1284+ while (1) console.log(Date.now());
1285+ }
1286+
1287+ vm.runInNewContext(
1288+ 'Promise.resolve().then(() => loop());',
1289+ { loop, console },
1290+ { timeout: 5, microtaskMode: 'afterEvaluate' }
1291+ );
1292+ ` ` `
1293+
1294+ In this case, the microtask scheduled through ` promise.then()` will be run
1295+ before returning from ` vm.runInNewContext()` , and will be interrupted
1296+ by the ` timeout` functionality . This applies only to code running in a
1297+ ` vm.Context` , so e .g . [` vm.runInThisContext()` ][] does not take this option.
1298+
1299+ Promise callbacks are entered into the microtask queue of the context in which
1300+ they were created . For example, if ` () => loop()` is replaced with just ` loop`
1301+ in the above example, then ` loop` will be pushed into the global microtask
1302+ queue, because it is a function from the outer (main ) context, and thus will
1303+ also be able to escape the timeout.
1304+
1305+ If asynchronous scheduling functions such as `process.nextTick()`,
1306+ `queueMicrotask()`, `setTimeout()`, `setImmediate()`, etc. are made available
1307+ inside a `vm.Context`, functions passed to them will be added to global queues,
1308+ which are shared by all contexts. Therefore, callbacks passed to those functions
1309+ are not controllable through the timeout either.
12581310
12591311[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`]: errors.html#ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING
12601312[`ERR_VM_MODULE_STATUS`]: errors.html#ERR_VM_MODULE_STATUS
0 commit comments