加载和运行 WebAssembly 代码

要在 JavaScript 中使用 WebAssembly,您首先需要在编译/实例化之前将模块加载到内存中。本文提供了有关可用于获取 WebAssembly 字节码的不同机制的参考,以及如何编译/实例化然后运行它的参考。

有哪些选项?

WebAssembly 尚未与<script type='module'>import语句集成,因此没有路径可以让浏览器使用导入为您获取模块。

较旧的WebAssembly.compile/WebAssembly.instantiate方法要求您在获取原始字节后创建一个包含 WebAssembly 模块二进制文件的ArrayBuffer,然后编译/实例化它。这类似于new Function(string),除了我们用字节数组缓冲区(WebAssembly 源代码)替换字符字符串(JavaScript 源代码)。

较新的WebAssembly.compileStreaming/WebAssembly.instantiateStreaming方法效率更高——它们直接对来自网络的原始字节流执行操作,无需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()与其他工作线程或窗口共享它,或者创建更多实例,则拥有模块很有用。

注意:第二种重载形式将WebAssembly.Module对象作为参数,并直接返回一个包含实例对象作为结果的 Promise。请参阅第二个重载示例

运行您的 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属性获取数组缓冲区,然后将其馈送到我们的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中看到此示例。