WebAssembly.Compile 在主线程上是不允许的,如果缓冲区大小大于 4KB

WebAssembly.Compile is disallowed on the main thread, if the buffer size is larger than 4KB

我正在尝试一个简单的 hello world WebAssembly 示例,但我无法理解我在 Chrome 59:

中看到的错误

RangeError: WebAssembly.Compile is disallowed on the main thread, if the buffer size is larger than 4KB. Use WebAssembly.compile, or compile on a worker thread.

src/wasm/counter.wasm:13
  10 | let wa;
  11 | const make = source => {
  12 |   // buffer should already be set
> 13 |   return wa = new Module(buffer);
  14 | };
  15 | 
  16 | const WebAssemblyModule = function(deps = {

我已按照 this tutorial, and I can build everything without error. I'm using create-react-app rewired with wasm-loader 中的步骤操作。

如果我使用本地构建的计数器模块,我会收到开头提到的错误。如果我尝试使用预构建版本(例如in this project),它工作正常。

当我构建我的模块时,我使用的是教程中指定的相同命令。具有工作模块的项目在其自述文件中列出了相同的命令:

emcc counter.c -O1 -o counter.wasm -s WASM=1 -s SIDE_MODULE=1

知道可能导致错误的原因吗?

一些浏览器限制了可以同步编译的模块的大小,因为这会阻塞主线程。正如错误消息所说,他们宁愿让你使用 WebAssembly.compile which returns a promise. I'd advise you to go further and use WebAssembly.instantiate which will both compile and instantiate asynchronously and, in some cases, generate higher performance code. See its documentation,签名是:

Promise<WebAssemblyInstantiatedSource>
  instantiate(BufferSource bytes [, importObject])

bytes 与您在上面传递给 Module 的缓冲区相同,而 importObject 与您传递给 Instance.[=25 的缓冲区相同=]


另一种选择是缩小 .wasm 文件。这非常脆弱,因为主线程大小限制是任意的,您无法真正控制生成代码的大小。您可以尝试使用 -O2-Os 进行编译,并且您可以使用 运行 binaryen 的优化器来尝试减小大小。您还可以将代码拆分为多个较小的模块,分别编译每个模块,然后动态 link 将它们组合在一起(共享导入/导出,并为所有模块使用相同的内存)。

但同样,这并不是您真正应该依赖的东西,而且您仍在阻塞主线程。


另一种选择是将所有代码移动到 WebWorker。您可以根据需要阻止它们,不需要 Promises.

通过Buffer创建一个URL对象,然后使用instantiateStreaming异步方法加载wasm。

const blob = new Blob([buffer], { type: "application/wasm" });
const url = URL.createObjectURL(blob);
wasm = await instantiateStreaming(fetch(url), {});