はじめに
ここでは、ESMバインディングを使用しているAssemblyScript側のコードから、ホスト側の関数をコールバックする方法を示します。
ESMバインディングの設定方法については、別記事「vite-react-comlink-worker-assemblyscript-webgpu-boilerplateを作った」の「設定項目の抜粋」のセクション内容を参照してください。
※別記事「vite-react-comlink-worker-assemblyscript-webgpu-boilerplateを作った」が、関連して開発したコードからの引用を含めて、3万字弱もの分量があるため、特に有用と思われる部分をこちらの記事に切り出しました。
AssemblyScript側のコード
AssemblyScript側のコードとして、次のようなものを書きます。
export function doSomething(value: u32): void{
myCallback(value * 1);
myCallback(value * 2);
myCallback(value * 3);
}
// @ts-ignore: decorator
@external("env", "myCallback")
export declare function myCallback(value: u32): void;
ここでは、doSomething
という関数が実装されています。
渡された値を、1倍、2倍、3倍した値にして、myCallback
関数を計3回呼ぶという内容です。
doSomething
関数には、export
がついています。これは、この関数が、AssemblyScript側からホスト側に公開されるものであることを示します。
なお、ここで使われているmyCallback
関数は、実際には、AssemblyScriptで実装されるのではなく、ホスト側(WebWorker側)で実装されるものになります。
上のコードの最後の2行が、このことを、@externalアノテーションを用いて設定する部分です。
「このAssemblyScriptのモジュールにおいて `myCallback`関数を呼び出すときには、その実体として、
`"env"`というモジュール名前空間の、`myCallback`関数を呼んでね!」
という意味の宣言になっています。
ちなみに、ここでの"env"
というのは、AssemblyScriptにおける特別なモジュール名前空間の名称で、ようするにglobalThis
のことを指しています(環境変数などのことではありません)。
なお、The AssemblyScript BookのHost bindingsのUsing ESM bindingsのページには、
@external("./otherfile.js", "myFunction")
というような表記で、任意のJSファイル内の関数を設定できるように書いてあるのですが、私が試した限りでは、うまく設定できませんでした。
現時点では、node_modules/assemblyscript/std/assembly.json
でチェックされる内容は、実際のAssemblyScriptのコンパイラで指定できる文法よりも古いため、@externalなどのデコレーターが正しく認識されずに「TS1206: Decorators are not valid here.」のエラーが表示されてしまいます。そうした場合には、上の例のように、下から3行目に「// @ts-ignore: decorator
」をつけて、エラーの出力を抑制しておきましょう。
ホスト側のコード
ホスト側では、まず、AssemblyScriptのコンパイラascで作成されたwasmファイルをimportで読み込みます。これをwasm
という名前で参照することとしています(importするパスは、あなたの開発している環境でのwasmファイルのビルド先に合わせて修正してください)。
次に、グローバル変数globalThis
に、myCallback
という名前で、今回コールバックしてほしい関数を紐づけしておきします。
そのあと、wasmに対して、関数doSomething
を実行します。このdoSomething
がAssemblyScript側の関数として実行される中で、ホスト側のmyCallback
がコールバックされることになります。
import * as wasm from "../../../build/callbackExample/assets";
(globalThis as any).myCallback = function(
value: number,
): void {
console.log(`callback: ${value}`);
};
wasm.doSomething(123);
実行結果
実行すると、次のような出力が得られるはずです。
callback: 123
callback: 246
callback: 369
まとめ
ホスト側からのWebAssembly側の関数を呼び出して、その結果を1回だけ受け取るのであれば、関数の返値で受け取れば良いのですが、結果を非同期的に複数回にわたって受け取りたい場合には、コールバック関数を用いることが必要になります。