使用 Deno 的一流 Wasm 支持

日本語|English|中国语
| 3 min read
Author: masahiro-kondo masahiro-kondoの画像
Information

为了覆盖更广泛的受众,这篇文章已从日语翻译而来。
您可以在这里找到原始版本。

简介

#

在 11 月的 Deno 2.1 版本中,加入了 First-class Wasm support 功能。

Deno 2.1: Wasm Imports and other enhancements

在以往的 Deno 中,可以使用 Web 标准的 API 加载和运行 Wasm,但从 2.1 之后,可以像普通模块一样,通过 import 使用 Wasm。

Wasm 的加载与运行:传统方法

#

以下是实例化本地的 WebAssembly 文件 add.wasm 并调用其公开的函数(add)的示例。

main.ts
const wasmInstance = await WebAssembly.instantiateStreaming(
  fetch(new URL("./add.wasm", import.meta.url)));

const { add } = wasmInstance.instance.exports as { add: (a: number, b: number) => number };

console.log(add(1, 2));

(虽然此处为了避免 TypeScript 的类型错误做了处理,)代码显得相当冗长。首先,需要通过 WebAssembly API 进行实例化,然后从实例中获取导出的函数(这里是 add)以供使用。

运行时需要使用 --allow-read 权限[1]

$ deno --allow-read main.ts
3
add.wasm 的创建方法

上述的 add.wasm 是通过以下的 wat 文件转换而成的。

add.wat
(module
  (func (export "add") (param i32 i32) (result i32)
    local.get 0
    local.get 1
    i32.add))

可以通过安装 WABT(WebAssembly Binary Toolkit)并使用 wat2wasm 命令,将此 wat 文件转换为 wasm 格式。

如果使用 wat2wasm 的演示网站,即使不在本地安装 WABT,也可以直接下载转换后的 Wasm 文件。

@wat2wasm demo

Information

关于加载与运行 Wasm 的 Web 标准 API,请参考以下文档:

加载和运行 WebAssembly 代码 - WebAssembly | MDN

有关在 Deno 中通过 Web 标准 API 使用 Wasm 的示例,可以参考以下文章。这是一篇比较早的内容。

Deno 入门 - 第五回 (使用 WebAssembly)

Wasm 的加载与运行:import

#

从 Deno 2.1 开始,可以通过 import 加载和运行 Wasm。

main.ts
import { add } from "./add.wasm";

console.log(add(1, 2));

代码变得非常简洁。运行也同样简单:

deno main.ts
Information

Deno 也会对 Wasm 模块进行类型检查。

从 Web Worker 使用

#

以前的连载文章中,曾介绍过利用 Web Worker 分布式执行 Wasm 代码的示例。当时的方法是,在主进程中预先编译 Wasm 并传递给 Worker,然后在 Worker 中实例化并执行。但现在,可以直接在 Worker 的代码中进行 import,使操作更加简便[2]

以下是 Worker 的代码。通过 add 模块,根据接收到的消息中的参数执行加法,并发送结果。

worker.ts
import { add } from "./add.wasm";

self.onmessage = e => {
  const result = add(e.data.a, e.data.b); // 执行 add
  postMessage(self.name + ": " + result); // 发送 Worker 名称和结果
};

以下是主进程的代码。创建多个 Worker 并注册接收消息的处理程序。

main.ts
const worker1 = createWorker("worker1");
const worker2 = createWorker("worker2");
handleWorkerMessage(worker1); // 注册 worker1 的消息接收处理程序
handleWorkerMessage(worker2); // 注册 worker2 的消息接收处理程序

worker1.postMessage({ a: 1, b: 2 }); // 向 worker1 发送消息
worker2.postMessage({ a: 3, b: 4 }); // 向 worker2 发送消息

function createWorker(name: string) {
  const worker = new Worker(new URL("./worker.ts", import.meta.url).href, { 
    type: "module",
    name: name 
  });
  return worker;
}

function handleWorkerMessage(worker: Worker) {
  worker.onmessage = e => {
    console.log(e.data);
  };
}

运行结果。可以从每个 Worker 获取使用 Wasm 执行的计算结果。

$ deno --allow-read main.ts
worker1: 3
worker2: 7

总结

#

以上是对 Deno 一流 Wasm 支持的简单测试。由于 Wasm 被纳入了 Deno 的模块图,因此 Deno 可以对其进行解析和缓存,从而实现更快速的加载和使用。

作为一流支持的特性,显然已经完全省去了原本类似“胶水代码”的部分。使用 Wasm 的门槛大大降低了。


  1. 从 Deno 1 系的最后一个版本 1.46 开始,可以省略 deno run 子命令。 ↩︎

  2. 这种方法主要是为了避免在每个 Worker 中重复编译。 ↩︎

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。