Using Deno's First-class Wasm Support

| 5 min read
Author: masahiro-kondo masahiro-kondoの画像
Information

To reach a broader audience, this article has been translated from Japanese.
You can find the original version here.

Introduction

#

In the November release of Deno 2.1, first-class Wasm support was added.

Deno 2.1: Wasm Imports and other enhancements

Previously in Deno, it was possible to load and execute Wasm using Web standard APIs, but from version 2.1 onwards, it can be used with import just like regular modules.

Loading and Executing Wasm: The Traditional Method

#

Below is an example of instantiating a WebAssembly called add.wasm placed locally and using the exposed function (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));

(This is also to prevent TypeScript type errors, but) it's quite a lengthy code. First, it instantiates with the WebAssembly API, then retrieves the exported function (in this case add) from the instance to make it usable.

Execution uses the --allow-read permission[1].

$ deno --allow-read main.ts
3
How to Create `add.wasm`

The above add.wasm is converted from the following wat file.

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

By installing WABT (WebAssembly Binary Toolkit) and using the wat2wasm command, you can convert this wat file to wasm format.

By using the wat2wasm demo site, you can download the converted wasm file without introducing WABT locally.

@wat2wasm demo

Information

For the Web standard API on loading and executing Wasm, please refer to the following document.

Loading and running WebAssembly code - WebAssembly | MDN

Usage of Wasm using Web standard APIs in Deno is also introduced in the following (though it's quite an old article).

Getting Started with Deno - Part 5 (Using WebAssembly)

Loading and Executing Wasm: Import

#

From Deno 2.1 onwards, Wasm can be loaded and executed using import.

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

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

It has become very simple. Execution is also simple.

deno main.ts
Information

Deno performs type checking even on Wasm modules.

Trying It from a Web Worker

#

In a previous article, I introduced an example of executing Wasm code in a distributed fashion using Web Workers. At that time, the method was to pre-compile the Wasm in the main process, pass it to the Worker, and have the Worker instantiate and execute it[2]. Now, since the Worker code can directly import, it has become easier.

The code on the Worker side. Using the add module, it performs addition processing based on parameters extracted from messages, and sends the result.

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

self.onmessage = e => {
  const result = add(e.data.a, e.data.b); // Execute add
  postMessage(self.name + ": " + result); // Send worker name and result
};

The main code. It just creates multiple workers and writes the processing when messages are received from the workers.

main.ts
const worker1 = createWorker("worker1");
const worker2 = createWorker("worker2");
handleWorkerMessage(worker1); // Register message receiving handler for worker1
handleWorkerMessage(worker2); // Register message receiving handler for worker2

worker1.postMessage({ a: 1, b: 2 }); // Send message to worker1
worker2.postMessage({ a: 3, b: 4 }); // Send message to 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);
  };
}

Execution result. We can obtain the processing results from each Worker using Wasm.

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

Conclusion

#

That's a quick experiment with Deno's first-class Wasm support. Wasm is incorporated into Deno's module graph, and due to Deno's analysis and caching, it seems to be usable at high speed.

As expected with first-class support, there are no glue code-like parts at all. The barriers to using Wasm have significantly lowered.


  1. Since Deno 1.46, the final release of the 1.x series, the deno run subcommand can be omitted. ↩︎

  2. Because I wanted to avoid compiling for each worker. ↩︎

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

recruit

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