載入並執行 WebAssembly 程式碼

要在 JavaScript 中使用 WebAssembly,您首先需要將模組載入到記憶體中,然後再進行編譯/例項化。本文件提供了用於獲取 WebAssembly 位元組碼的各種機制的參考,以及如何編譯/例項化然後執行它。

有哪些選項?

WebAssembly 尚未與 <script type='module'>import 語句整合,因此沒有辦法讓瀏覽器使用 import 來為您獲取模組。

較舊的 WebAssembly.compile/WebAssembly.instantiate 方法要求您在獲取原始位元組後建立一個包含 WebAssembly 模組二進位制檔案的 ArrayBuffer,然後對其進行編譯/例項化。這類似於 new Function(string),只是我們將字串(JavaScript 原始碼)替換為位元組陣列(WebAssembly 原始碼)。

較新的 WebAssembly.compileStreaming/WebAssembly.instantiateStreaming 方法效率更高——它們直接在來自網路的原始位元組流上執行操作,從而省去了 ArrayBuffer 步驟。

那麼,我們如何將這些位元組載入到 ArrayBuffer 中並進行編譯呢?接下來的幾部分將對此進行解釋。

使用 Fetch

Fetch 是一個方便的現代 API,用於獲取網路資源。

獲取 Wasm 模組最快捷、最高效的方法是使用較新的 WebAssembly.instantiateStreaming() 方法,它可以將 fetch() 呼叫作為第一個引數,並一次性完成模組的獲取、編譯和例項化,直接訪問從伺服器流式傳輸過來的原始位元組碼。

js
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
  (results) => {
    // Do something with the results!
  },
);

如果我們使用不支援直接流的較舊的 WebAssembly.instantiate() 方法,我們需要額外一步將獲取的位元組碼轉換為 ArrayBuffer,如下所示:

js
fetch("module.wasm")
  .then((response) => response.arrayBuffer())
  .then((bytes) => WebAssembly.instantiate(bytes, importObject))
  .then((results) => {
    // Do something with the results!
  });

關於 instantiate() 過載的說明

WebAssembly.instantiate() 函式有兩種過載形式——上面展示的一種接受要編譯的位元組碼作為引數,並返回一個 Promise,該 Promise 解析為一個物件,其中包含編譯後的模組物件和它的一個例項化例項。該物件看起來像這樣:

js
({
  module: Module, // The newly compiled WebAssembly.Module object,
  instance: Instance, // A new WebAssembly.Instance of the module object
});

注意:通常我們只關心例項,但為了方便快取模組、透過 postMessage() 與其他 Worker 或視窗共享,或者建立更多例項,保留模組物件是很有用的。

注意:第二種過載形式接受一個 WebAssembly.Module 物件作為引數,並直接返回一個包含例項物件的 Promise。請參閱 Second overload example

執行您的 WebAssembly 程式碼

一旦您在 JavaScript 中獲得了 WebAssembly 例項,就可以開始使用它透過 WebAssembly.Instance.exports 屬性匯出的功能。您的程式碼可能看起來像這樣:

js
WebAssembly.instantiateStreaming(fetch("myModule.wasm"), importObject).then(
  (obj) => {
    // Call an exported function:
    obj.instance.exports.exported_func();

    // or access the buffer contents of an exported memory:
    const dv = new DataView(obj.instance.exports.memory.buffer);

    // or access the elements of an exported table:
    const table = obj.instance.exports.table;
    console.log(table.get(0)());
  },
);

注意:有關 WebAssembly 模組如何匯出功能的更多資訊,請閱讀 使用 WebAssembly JavaScript API理解 WebAssembly 文字格式

使用 XMLHttpRequest

XMLHttpRequest 比 Fetch 稍舊,但仍然可以很好地用於獲取型別化陣列。同樣,假設我們的模組名為 simple.wasm

  1. 建立一個新的 XMLHttpRequest() 例項,並使用其 open() 方法開啟一個請求,將請求方法設定為 GET,並宣告要獲取的檔案路徑。
  2. 關鍵部分是使用 responseType 屬性將響應型別設定為 'arraybuffer'
  3. 接下來,使用 XMLHttpRequest.send() 傳送請求。
  4. 然後,我們使用 load 事件處理程式在響應下載完成後呼叫一個函式——在該函式中,我們從 response 屬性獲取 ArrayBuffer,然後將其饋送到我們的 WebAssembly.instantiate() 方法中,就像我們使用 Fetch 時一樣。

最終程式碼如下:

js
const request = new XMLHttpRequest();
request.open("GET", "simple.wasm");
request.responseType = "arraybuffer";
request.send();

request.onload = () => {
  const bytes = request.response;
  WebAssembly.instantiate(bytes, importObject).then((results) => {
    results.instance.exports.exported_func();
  });
};

注意:您可以在 xhr-wasm.html 中看到此示例的實際應用。