This repository contains a few examples of exchanging messages between JavaScript workers in a blocking / synchronous way.
This technique uses two mechanisms:
- Atomics.wait to synchronously wait for the worker to do the work and send the data. This relies on a
SharedArrayBuffer
, but could potentially work with justAtomics.pause()
and no shared buffers. - receiveMessageOnPort to synchronously read the data sent by the worker.
Run the code with:
$ node --disable-warning=ExperimentalWarning --experimental-transform-types ./node-receiveMessageOnPort-simple/parent.ts
[child] calling blockingRpc to parent
[parent] onmessage [ 9, 3, 7 ]
[parent] got result [ 3, 7, 9 ]
[child] got result { message: [ 3, 7, 9 ] }
This extends the Comlink library with a exposeSync
/wrapSync
API that enables
synchronous method calls to another worker. It benefits from all the Comlink goodness,
such as transfer handles, propagating errors, etc.
Run the code with:
$ node --disable-warning=ExperimentalWarning --experimental-
transform-types ./node-receiveMessageOnPort-comlink/parent.ts
[parent] started
[parent] Worker stdout: a
[parent] wrapSync() called
[comlink-sync] canHandle() called { obj: 2 }
[comlink-sync] canHandle() called { obj: 3 }
[parent] add() called { sum: 5 }
[comlink-sync] canHandle() called { obj: 2 }
[comlink-sync] canHandle() called { obj: 3 }
[parent] add() called { sum2: 5 }
[comlink-sync] canHandle() called { obj: 'https://wp.org' }
Lacking receiveMessageOnPort
and easy access to SharedArrayBuffer
in web browsers, I looked for another technique. Unfortunately, I'm yet to find one that works.
During the development of the PHP-WASM project, integrating asynchronous calls with the synchronous PHP code was a big challenge. In JSPI-enabled runtimes, it all just worked. However, in Asyncify-only runtimes, such as Safari of Node.js < 23, we've spent hundreds of hours tweaking the compilation flags to make it work – just see all these discussions.
The final straw was fcntl() integration. Despite a ton of effort, nothing seemed to work. I finally gave up and decided to just ditch Asyncify and rely on synchronous calls instead.
This is how this repository came to life.
GPLv2