async、await 與模組


重新再來一次吧!回頭看一下〈載入、運行 WebAssembly〉,無論是 Fetch,或者是 WebAssembly 的 instantiateStreaming 等函式,都傳回了 Promise,許多文件也是這麼介紹模組的載入、實例化等過程。

別忘了,在 ECMAScript 7 中納入了 asyncawait,這表示在瀏覽器支援的情況下,是可以運用 asyncawait 來令這個過程更為直覺而容易撰寫!

接下來的內容,可以用來與〈載入、運行 WebAssembly〉中的範例做個對照。

例如,就這個簡單的 WebAssembly 來說:

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

使用 asyncawait,來實作載入、實例化模組並呼叫函式的過程,可以寫為:

<!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>

若沒有需要獲取 Module 實例,使用 ES6 解構語法直接取得 Instance 是很方便的做法,就整個流程的效率來說,這也是建議的寫法!

WebAssembly.instantiateStreaming 會透過傳入的 Promise 取得儲存著 .wasm 的 ArrayBuffer,然後再進行實例化,因此上例相當於:

(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)); 
})();

如果想要 WebAssembly.Module 的實例,可以使用 WebAssembly.compileStreaming,例如:

(async () => {
    const module = await WebAssembly.compileStreaming(fetch('program.wasm'));
    // 也許用 module 做點其他的事....
    // Bla...bla..

    // 這是個阻斷操作
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

因為 new WebAssembly.Instance(module) 會是個阻斷操作,如果沒必要取得 Module 實例,就建議不用如上撰寫了。

實際上,WebAssembly.compileStreaming 會取得 ArrayBuffer,然後使用 WebAssembly.compile 來編譯、生成 Module 實例,也就是上例相當於:

(async () => {
    const resp = await fetch('program.wasm');
    const wasm = await resp.arrayBuffer();
    const module = await WebAssembly.compile(wasm);
    // 也許用 module 做點其他的事....
    // Bla...bla..

    // 這是個阻斷操作
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

若手中已經有個 ArrayBuffer,也可以用來建構 WebAssembly.Module 實例:

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

    // 這是個阻斷操作
    const module = new WebAssembly.Module(wasm);

    // 這是個阻斷操作
    const instance = new WebAssembly.Instance(module);
    console.log(instance.exports.add(1, 2)); 
})();

如果想要善用 JavaScript 非同步的特性,就要留意一下阻斷操作的部份是否真的是必需的!