@@ -1329,6 +1329,228 @@ from the running process and will preload the same preload scripts as the main
1329
1329
thread. If the preload script unconditionally launches a worker thread, every
1330
1330
thread spawned will spawn another until the application crashes.
1331
1331
1332
+ ## Synchronous Workers
1333
+
1334
+ > Stability: 1 - Experimental
1335
+
1336
+ ### Class: ` SynchronousWorker`
1337
+
1338
+ <!-- YAML
1339
+ added: REPLACEME
1340
+ -->
1341
+
1342
+ * Extends: {EventEmitter}
1343
+
1344
+ A ` SynchronousWorker` is effectively a Node.js environment that runs within the
1345
+ same thread.
1346
+
1347
+ ` ` ` cjs
1348
+ const { SynchronousWorker } = require (' node:worker_threads' );
1349
+ const w = new SynchronousWorker ();
1350
+ const myAsyncFunction = w .createRequire (__filename )(' my-module' );
1351
+ const response = w .runLoopUntilPromiseResolved (myAsyncFunction (' http://example.org' ));
1352
+ const text = w .runLoopUntilPromiseResolved (response .text ());
1353
+ console .log (text);
1354
+ ` ` `
1355
+
1356
+ #### ` new SynchronousWorker ([options])`
1357
+
1358
+ <!-- YAML
1359
+ added: REPLACEME
1360
+ -->
1361
+
1362
+ * ` options` {Object}
1363
+ * ` sharedEventLoop` {boolean} When ` true ` , use the same event loop as the
1364
+ outer Node.js instance. If this is passed, the ` runLoop ()` and
1365
+ ` runLoopUntilPromiseResolved ()` methods become unavailable.
1366
+ **Default:** ` false ` .
1367
+ * ` sharedMicrotaskQueue` {boolean} When true, use the same microtask queue as
1368
+ the outer Node.js instance. This is used for resolving promises created in
1369
+ the inner context, including those implicitly generated by ` async / await ` .
1370
+ If this is passed, the ` runLoopUntilPromiseResolved ()` method becomes
1371
+ unavailable. **Default:** ` false ` .
1372
+
1373
+ While setting ` sharedEventLoop` to ` false ` and ` sharedMicrotaskQueue` to ` true `
1374
+ is accepted, they typically do not make sense together.
1375
+
1376
+ #### ` synchronousWorker .runLoop ([mode])`
1377
+
1378
+ <!-- YAML
1379
+ added: REPLACEME
1380
+ -->
1381
+
1382
+ * ` mode` {string} One of either ` ' default' ` , ` ' once' ` , or ` ' nowait' ` .
1383
+
1384
+ Spin the event loop of the inner Node.js instance. ` mode` can be either
1385
+ ` default` , ` once` or ` nowait` . See the [libuv documentation for ` uv_run ()` ][]
1386
+ for details on these modes.
1387
+
1388
+ #### ` synchronousWorker .runLoopUntilPromiseResolved (promise)`
1389
+
1390
+ <!-- YAML
1391
+ added: REPLACEME
1392
+ -->
1393
+
1394
+ * ` promise` {Promise}
1395
+
1396
+ Spin the event loop of the innsert Node.js instance until a specific ` Promise `
1397
+ is resolved.
1398
+
1399
+ #### ` synchronousWorker .runInWorkerScope (fn)`
1400
+
1401
+ <!-- YAML
1402
+ added: REPLACEME
1403
+ -->
1404
+
1405
+ * ` fn` {Function}
1406
+
1407
+ Wrap ` fn` and run it as if it were run on the event loop of the inner Node.js
1408
+ instance. In particular, this ensures that Promises created by the function
1409
+ itself are resolved correctly. You should generally use this to run any code
1410
+ inside the innert Node.js instance that performs asynchronous activity and that
1411
+ is not already running in an asynchronous context (you can compare this to
1412
+ the code that runs synchronously from the main file of a Node.js application).
1413
+
1414
+ #### ` synchronousWorker .loopAlive `
1415
+
1416
+ <!-- YAML
1417
+ added: REPLACEME
1418
+ -->
1419
+
1420
+ * Type: {boolean}
1421
+
1422
+ This is a read-only boolean property indicating whether there are currently any
1423
+ items on the event loop of the inner Node.js instance.
1424
+
1425
+ #### ` synchronousWorker .stop ()`
1426
+
1427
+ <!-- YAML
1428
+ added: REPLACEME
1429
+ -->
1430
+
1431
+ Interrupt any execution of code inside the inner Node.js instance, i.e.
1432
+ return directly from a ` .runLoop ()` , ` .runLoopUntilPromiseResolved ()` or
1433
+ ` .runInWorkerScope ()` call. This will render the Node.js instance unusable
1434
+ and is generally comparable to running ` process .exit ()` .
1435
+
1436
+ This method returns a ` Promise ` that will be resolved when all resources
1437
+ associated with this Node.js instance are released. This ` Promise ` resolves on
1438
+ the event loop of the _outer_ Node.js instance.
1439
+
1440
+ #### ` synchronousWorker .createRequire (filename)`
1441
+
1442
+ <!-- YAML
1443
+ added: REPLACEME
1444
+ -->
1445
+
1446
+ * ` filename` {string}
1447
+
1448
+ Create a ` require ()` function that can be used for loading code inside the
1449
+ inner Node.js instance.
1450
+
1451
+ #### ` synchronousWorker .globalThis `
1452
+
1453
+ <!-- YAML
1454
+ added: REPLACEME
1455
+ -->
1456
+
1457
+ * Type: {Object}
1458
+
1459
+ Returns a reference to the global object of the inner Node.js instance.
1460
+
1461
+ #### ` synchronousWorker .process `
1462
+
1463
+ <!-- YAML
1464
+ added: REPLACEME
1465
+ -->
1466
+
1467
+ * Type: {Object}
1468
+
1469
+ Returns a reference to the ` process ` object of the inner Node.js instance.
1470
+
1471
+ ### FAQ
1472
+
1473
+ #### What does a SynchronousWorker do?
1474
+
1475
+ Creates a new Node.js instance, using the same thread and the same JS heap.
1476
+ You can create Node.js API objects, like network sockets, inside the new
1477
+ Node.js instance, and spin the underlying event loop manually.
1478
+
1479
+ #### Where did SynchronousWorker come from?
1480
+
1481
+ ` SynchronousWorker` was originally developer by Node.js core contributor
1482
+ Anna Henningsen and published as a separate module [` synchronous- worker` ][] on
1483
+ npm under the MIT license. It was integrated into Node.js core with Anna's
1484
+ permission. The majority of the code, documentation, and tests were adopted
1485
+ almost verbatim from the original module.
1486
+
1487
+ #### Why would I use a SynchronousWorker?
1488
+
1489
+ The most common use case is probably running asynchronous code synchronously,
1490
+ in situations where doing so cannot be avoided (even though one should try
1491
+ really hard to avoid it). Another popular npm package that does this is
1492
+ [` deasync` ][], but ` deasync`
1493
+
1494
+ * solves this problem by starting the event loop while it is already running
1495
+ (which is explicitly _not_ supported by libuv and may lead to crashes)
1496
+ * doesn’t allow specifying _which_ resources or callbacks should be waited for,
1497
+ and instead allows everything inside the current thread to progress.
1498
+
1499
+ #### How can I avoid using SynchronousWorker?
1500
+
1501
+ If you do not need to directly interact with the objects inside the inner
1502
+ Node.js instance, a lot of the time Worker threads together with
1503
+ [` Atomics .wait ()` ][] will give you what you need.
1504
+
1505
+ #### My async functions/Promises/… don’t work
1506
+
1507
+ If you run a ` SynchronousWorker` with its own microtask queue (i.e. in default
1508
+ mode), code like this will not work as expected:
1509
+
1510
+ ` ` ` cjs
1511
+ const { SynchronousWorker } = require (' node:worker_threads' );
1512
+ const w = new SynchronousWorker ();
1513
+ let promise;
1514
+ w .runInWorkerScope (() => {
1515
+ promise = (async () => {
1516
+ return w .createRequire (__filename )(' node-fetch' )(' ...' );
1517
+ })();
1518
+ });
1519
+ w .runLoopUntilPromiseResolved (promise);
1520
+ ` ` `
1521
+
1522
+ The reason for this is that ` async ` functions (and Promise ` .then ()` handlers)
1523
+ add their microtasks to the microtask queue for the Context in which the
1524
+ async function (or ` .then ()` callback) was defined, and not the Context in which
1525
+ the original ` Promise ` was created. Put in other words, it is possible for a
1526
+ ` Promise ` chain to be run on different microtask queues.
1527
+
1528
+ While this behavior may be counterintuitive, it is what the V8 engine does,
1529
+ and is not under the control of Node.js.
1530
+
1531
+ What this means is that you will need to make sure that the functions are
1532
+ compiled in the Context in which they are supposed to be run; the two main
1533
+ ways to achieve that are to:
1534
+
1535
+ * Put them in a separate file that is loaded through ` w .createRequire ()`
1536
+ * Use ` w .createRequire (__filename )(' vm' ).runInThisContext ()` to manually compile
1537
+ the code for the function in the Context of the target Node.js instance.
1538
+
1539
+ For example:
1540
+
1541
+ ` ` ` cjs
1542
+ const { SynchronousWorker } = require (' node:worker_threads' );
1543
+ const w = new SynchronousWorker ();
1544
+ const req = w .createRequire (__filename );
1545
+ let promise;
1546
+ w .runInWorkerScope (() => {
1547
+ promise = req (' vm' ).runInThisContext (` (async(req) => {
1548
+ return await req('node-fetch')('...');
1549
+ })` )(req);
1550
+ });
1551
+ w .runLoopUntilPromiseResolved (promise);
1552
+ ` ` `
1553
+
1332
1554
[Addons worker support]: addons.md#worker-support
1333
1555
[ECMAScript module loader]: esm.md#data-imports
1334
1556
[HTML structured clone algorithm]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm
@@ -1341,6 +1563,7 @@ thread spawned will spawn another until the application crashes.
1341
1563
[` -- max- semi- space- size` ]: cli.md#--max-semi-space-sizesize-in-megabytes
1342
1564
[` ArrayBuffer ` ]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer
1343
1565
[` AsyncResource` ]: async_hooks.md#class-asyncresource
1566
+ [` Atomics .wait ()` ]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait
1344
1567
[` Buffer .allocUnsafe ()` ]: buffer.md#static-method-bufferallocunsafesize
1345
1568
[` Buffer` ]: buffer.md
1346
1569
[` ERR_MISSING_MESSAGE_PORT_IN_TRANSFER_LIST ` ]: errors.md#err_missing_message_port_in_transfer_list
@@ -1354,6 +1577,7 @@ thread spawned will spawn another until the application crashes.
1354
1577
[` Worker constructor options`]: #new-workerfilename-options
1355
1578
[`Worker`]: #class-worker
1356
1579
[`data:` URL]: https:// developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
1580
+ [`deasync`]: https:// www.npmjs.com/package/deasync
1357
1581
[`fs.close ()`]: fs.md#fsclosefd-callback
1358
1582
[`fs.open()`]: fs.md#fsopenpath-flags-mode-callback
1359
1583
[`markAsUntransferable()`]: #workermarkasuntransferableobject
@@ -1378,6 +1602,7 @@ thread spawned will spawn another until the application crashes.
1378
1602
[`require('node:worker_threads').parentPort`]: #workerparentport
1379
1603
[`require('node:worker_threads').threadId`]: #workerthreadid
1380
1604
[`require('node:worker_threads').workerData`]: #workerworkerdata
1605
+ [`synchronous-worker`]: https:// github.com/addaleax/synchronous-worker
1381
1606
[`trace_events`]: tracing.md
1382
1607
[`v8.getHeapSnapshot()`]: v8.md#v8getheapsnapshot
1383
1608
[`vm`]: vm.md
@@ -1390,4 +1615,5 @@ thread spawned will spawn another until the application crashes.
1390
1615
[browser `MessagePort`]: https:// developer.mozilla.org/en-US/docs/Web/API/MessagePort
1391
1616
[child processes]: child_process.md
1392
1617
[contextified]: vm.md#what-does-it-mean-to-contextify-an-object
1618
+ [libuv documentation for `uv_run()`]: http:// docs.libuv.org/en/v1.x/loop.html#c.uv_run
1393
1619
[v8.serdes]: v8.md#serialization-api
0 commit comments