載入並執行 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() 呼叫作為第一個引數,並一次性完成模組的獲取、編譯和例項化,直接訪問從伺服器流式傳輸過來的原始位元組碼。
WebAssembly.instantiateStreaming(fetch("simple.wasm"), importObject).then(
(results) => {
// Do something with the results!
},
);
如果我們使用不支援直接流的較舊的 WebAssembly.instantiate() 方法,我們需要額外一步將獲取的位元組碼轉換為 ArrayBuffer,如下所示:
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 解析為一個物件,其中包含編譯後的模組物件和它的一個例項化例項。該物件看起來像這樣:
({
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 屬性匯出的功能。您的程式碼可能看起來像這樣:
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:
- 建立一個新的
XMLHttpRequest()例項,並使用其open()方法開啟一個請求,將請求方法設定為GET,並宣告要獲取的檔案路徑。 - 關鍵部分是使用
responseType屬性將響應型別設定為'arraybuffer'。 - 接下來,使用
XMLHttpRequest.send()傳送請求。 - 然後,我們使用
load事件處理程式在響應下載完成後呼叫一個函式——在該函式中,我們從response屬性獲取 ArrayBuffer,然後將其饋送到我們的WebAssembly.instantiate()方法中,就像我們使用 Fetch 時一樣。
最終程式碼如下:
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 中看到此示例的實際應用。