async, await and modules


Let's review Loading and running WebAssembly, Some functions in Fetch and WebAssembly API, such as instantiateStreaming, return Promise. You use then methods to handle async operations like synchronous styles.

ECMAScript 7 provides async and await. If your browsers support ES7, you can use them to work with promises in a more comfort fashion.

For comparison, the following examples will show how to use async and await to do things like examples in Loading and running WebAssembly.

If you have a simple module:

(module
  (func $add (param $lhs i32) (param $rhs i32) (result i32)
    get_local $lhs
    get_local $rhs
    i32.add)
  (export "add" (func $add))
)

Using async and await to load, instantizate the module and invoke the exported function can be written as follow.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
  </head>
  <body>
    <script>    

    (async () => {
        const wasm = fetch('program.wasm');
        const {instance} = await WebAssembly.instantiateStreaming(wasm);
        console.log(instance.exports.add(1, 2)); 
    })();

    </script>
  </body>
</html>

It's the recommended way if the Module instance is not required.

WebAssembly.instantiateStreaming would take out the ArrayBuffer instance of “.wasm“ from the Promise object and instantiate the module. The following code has the same effect as the above.

(async () => {
    const resp = await fetch('program.wasm');
    const wasm = await resp.arrayBuffer();
    const {instance} = await WebAssembly.instantiate(wasm);
    console.log(instance.exports.add(1, 2)); 
})();

If the instance of WebAssembly.Module is required, you may use WebAssembly.compileStreaming. For example:

(async () => {
    const module = await WebAssembly.compileStreaming(fetch('program.wasm'));
    // use module to do things....
    // Bla...bla..

    // block the main thread until it completes
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

new WebAssembly.Instance(module) would block the main thread until it completes so don't do this if WebAssembly.Module is required.

In fact, WebAssembly.compileStreaming will take out the ArrayBuffer and use WebAssembly.compile to compile it to a WebAssembly.Module instance. The following code has the same effect as the above.

(async () => {
    const resp = await fetch('program.wasm');
    const wasm = await resp.arrayBuffer();
    const module = await WebAssembly.compile(wasm);
    // use module to do things....
    // Bla...bla..

    // block the main thread until it completes
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

If you have ArrayBuffer, you can construct a WebAssembly.Module object directly.

(async () => {
    const resp = await fetch('program.wasm');
    const wasm = await resp.arrayBuffer();

    // block the main thread until it completes
    const module = new WebAssembly.Module(wasm);

    // block the main thread until it completes
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

In short, use asynchronous APIs to avoid blocking the main thread. Don't use synchronous code not required.