Receive data synchronously with WebWorker?

While I understand that JavaScript is essentially single-threaded and is usually frowned upon by such things, I am wondering if there is a way to make the WebWorker wait for some data to be available from the main thread without destroying the WebWorker's call stack.

Since this is for a fun project, I can use new technologies and things that won't work reliably in older browsers, and I don't mind esoteric hacks as long as they work.

Some other solutions I was thinking about:

  • Continuously try LocalStorage in a loop until there is data with the given key. This would seem to work because LocalStorage updates by other threads should be visible to the current thread, even if polled in a loop, judging by all the discussion about LocalStorage thread safety and having multiple tabs written to the same LocalStorage key. The disadvantage of this approach is that it is not "pending"; The worker thread is still consuming the full CPU capture in LocalStorage. Although LocalStorage is usually implemented with locks, it is not possible to hold a LocalStorage lock for long periods of time (the lock is released upon return getItem

    or setItem

    ).

  • ECMAScript 6 yield

    . This doesn't work here because generator functions require all the functions in the call stack to execute (unless you want to give in). The place where I want to pause my WebWorker has a call stack that contains WebAssembly functions that cannot be marked as generator functions.

  • IndexedDB. This doesn't work because IndexedDB doesn't support synchronous queries.

I know this similar question, but this question is event specific onmessage

and was asked in 2012, before yield

WebAssembly was introduced .

Is there a way to somehow simulate blocking on the WebWorker thread or otherwise so that it waits for some data to be available?

+3


source to share


1 answer


JavaScript SharedArrayBuffer sounds perfect for you:

  • new technologies: just moved to stage 4 at TC39 January meeting (during ES2017)
  • will not work reliably in older browsers (older versions have a different API or no implementation)
  • esoteric hack ( similar to C ++ memory model )
  • works

For your purpose, you want the WebWorker to wait until the data is available. With help SharedArrayBuffer

you can use spinloop ( Atomics.load

as long as the value doesn't change), but it would be better to use Atomics.wait

while another worker is sending to you Atomics.wake

. This later API is heavily inspired by the Linux futex and will not spin uselessly if the expected value is not available.



// Main program:
var w = new Worker("worker.js")
var sab = new SharedArrayBuffer(1024);
w.postMessage(sab);
var i = new Int32Array(sab);
// Maybe wait for worker.js to message back that it ready through onmessage?
//
// Fill i with some data!
// ...
//
// Notify one worker, at location 0, with value 1.
Atomics.store(i, 0, 1);
Atomics.wake(i, 0, /* notify count */ 1);


// worker.js:
var sab;
var i;
onmessage = function (ev) {
    sab = ev.data;
    var i = new Int32Array(sab);
}
// Maybe tell the main program we're ready through postMessage?
//
// ...
// Wait until location 0 isn't value 0
Atomics.wait(i, 0, 0);

      

Remember: it's a bad idea to block the main thread! If you do this, your site will be out of date. Your question was asking a question about blocking a worker, but readers might be interested in waiting on the main thread. Do not!

A very similar and compatible API will eventually be available in WebAssembly . Here is a preliminary draft of the project . When I say compatible: we expect WebAssembly to be able to use the same SharedArrayBuffer

as JavaScript, and both can share it seamlessly.

+4


source







All Articles