How to use asynchronous web apis from WebAssembly

February 19, 2023

Why this is a problem?

See Using asynchronous web APIs from WebAssembly for the reasons.

Why another article?

Because it doesn't always work all the time.

You have to specify correct ASYNCIFY_STACK_SIZE if the call stack is deep and unpredictable.

Web solutions

Attention: both the web solutions depends on https to work in production environment.

I learned all these from the awesome Partytown project.

Use SharedArrayBuffer

// in the web worker thread

const size = 4 + 1;

// make sure you set the proper size,
// because the main thread will set data to the same `sab`.
const length = size * 4;
const sab = new SharedArrayBuffer(length);

// the length must be divisible by 4,
// or next line will throw a RangeError.
const int32 = new Int32Array(sab);

self.postMessage({ sab });

// wait on position 0 value
Atomics.wait(int32, 0, 0);
// in the main thread
const worker = new Worker("http://example/worker.js");
worker.onmessage = function (e) {
  if (e.data.sab) {
    const int32 = new Int32Array(e.data.sab);

    const size = 4;

    // update with data
    int32.set(Array(size).fill(1024), 1);

    // set a new value at position 0
    Atomics.store(int32, 0, 1);

    // notify the waiting thread
    Atomics.notify(int32, 0, 1);
  }
};

You can find the full example in StackBlitz SharedArrayBuffer demo. Open the developer tools, you will see some log like this: StackBlitz SharedArrayBuffer Demo

Full guide to set up SharedArrayBuffer, see Atomics.

More deep details about Atomics, see Avoiding race conditions in SharedArrayBuffers with Atomics.

Use Service Worker

You can use service worker to intercept the synchronous XHR request, and do whatever you want using the asynchronous web api. After you got the result, just return the response.

Other tricks

  • Prepare data in advance, may solve the problem depending on the situation
  • Use backend server with synchronous XHR request, somehow complicated, but should work when the transmitted data size is small.

Hope

The JavaScript Promise Integration (JSPI) API allows WebAssembly applications that were written assuming that access to external functionality was synchronous to operate smoothly in an environment where much of the desired functionality is asynchronous.

See JSPI for more detail.


Profile picture

Written by Priestch who lives and works in Xi'an building useful things. You can follow him on Github