加载并运行 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 中看到此示例的实际应用。