@@ -188,6 +188,9 @@ overhead.
188
188
<!-- YAML
189
189
added: v0.3.1
190
190
changes:
191
+ - version: REPLACEME
192
+ pr-url: https://github.com/nodejs/node/pull/34023
193
+ description: The `microtaskMode` option is supported now.
191
194
- version: v10.0.0
192
195
pr-url: https://github.com/nodejs/node/pull/19016
193
196
description: The `contextCodeGeneration` option is supported now.
@@ -225,6 +228,10 @@ changes:
225
228
` EvalError ` . ** Default:** ` true ` .
226
229
* ` wasm ` {boolean} If set to false any attempt to compile a WebAssembly
227
230
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.
228
235
* Returns: {any} the result of the very last statement executed in the script.
229
236
230
237
First contextifies the given ` contextObject ` , runs the compiled code contained
@@ -842,6 +849,9 @@ function with the given `params`.
842
849
<!-- YAML
843
850
added: v0.3.1
844
851
changes:
852
+ - version: REPLACEME
853
+ pr-url: https:// github.com/nodejs/node/pull/34023
854
+ description: The `microtaskMode` option is supported now.
845
855
- version: v10.0.0
846
856
pr-url: https:// github.com/nodejs/node/pull/19398
847
857
description: The first argument can no longer be a function.
@@ -867,6 +877,10 @@ changes:
867
877
`EvalError`. **Default:** `true`.
868
878
* `wasm` {boolean} If set to false any attempt to compile a WebAssembly
869
879
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.
870
884
* Returns: {Object } contextified object.
871
885
872
886
If given a ` contextObject` , the ` vm.createContext()` method will [prepare
@@ -998,6 +1012,9 @@ console.log(contextObject);
998
1012
<!-- YAML
999
1013
added: v0.3.1
1000
1014
changes:
1015
+ - version: REPLACEME
1016
+ pr-url: https://github.com/nodejs/node/pull/34023
1017
+ description: The `microtaskMode` option is supported now.
1001
1018
- version: v10.0.0
1002
1019
pr-url: https://github.com/nodejs/node/pull/19016
1003
1020
description: The `contextCodeGeneration` option is supported now.
@@ -1064,6 +1081,10 @@ changes:
1064
1081
* Returns: {Module Namespace Object|vm.Module} Returning a `vm.Module` is
1065
1082
recommended in order to take advantage of error tracking, and to avoid
1066
1083
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.
1067
1088
* Returns: {any} the result of the very last statement executed in the script.
1068
1089
1069
1090
The `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
1220
1241
associating it with the ` contextObject` is what this document refers to as
1221
1242
" contextifying" the object.
1222
1243
1223
- ## Timeout limitations when using ` process.nextTick() ` , promises, and ` queueMicrotask() `
1244
+ ## Timeout interactions with asynchronous tasks and Promises
1224
1245
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 .
1230
1251
1231
1252
For example, the following code executed by ` vm.runInNewContext()` with a
1232
1253
timeout 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:
1236
1257
const vm = require('vm');
1237
1258
1238
1259
function loop() {
1260
+ console.log('entering loop');
1239
1261
while (1) console.log(Date.now());
1240
1262
}
1241
1263
1242
1264
vm.runInNewContext(
1243
- 'Promise.resolve().then(loop);',
1265
+ 'Promise.resolve().then(() => loop() );',
1244
1266
{ loop, console },
1245
1267
{ timeout: 5 }
1246
1268
);
1269
+ // This prints *before* 'entering loop' (!)
1270
+ console.log('done executing');
1247
1271
` ` `
1248
1272
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 ` :
1251
1275
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.
1254
1306
1255
1307
[`ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING`]: errors.html#ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING
1256
1308
[`ERR_VM_MODULE_STATUS`]: errors.html#ERR_VM_MODULE_STATUS
0 commit comments