JavaScriptでGoの関数を呼び出す方法を紹介します。
Go側
簡単な関数を作ってみましょう。
フォルダを作ってGoのmain関数を作る
# フォルダを作る
mkdir js-call-wasm
# 初期化
go mod init example.com/js-call-wasm
VSCodeで開いて、main.go
を作ります。
package main
import "syscall/js"
func add(a float32, b float32) float32 {
return a + b
}
func main() {
js.Global().Set("add", js.FuncOf(add))
}
JavaScriptを呼び出す及び呼び出されるために、こちらsyscall/js
を使っています。一番簡単な方法はglobalにadd
関数を追加することにします(js.Global().Set
)。
VSCodeの赤線問題
VSCodeのGoプラグインがうまくGOOS
とGOARCH
を識別できないため、syscall/js
に赤線が表示されます。
エラーメッセージはこちらです。
could not import syscall/js (cannot find package "syscall/js" in any of
/usr/local/go/src/syscall/js
解決方法は.vscode/settings.json
に下記の設定の追加することです。
{
"gopls": {
"build.env": {
"GOOS": "js",
"GOARCH": "wasm"
}
}
}
そしてReload Window
で赤線が消えてしまいます。
Go関数を改造
Goのオリジナルの関数を直接呼び出せません。WebAssemblyのため下記のように改造する必要があります。
package main
import "syscall/js"
// JS用に改造func add(this js.Value, inputs []js.Value) interface{} { return inputs[0].Float() + inputs[1].Float()}
func main() {
js.Global().Set("add", js.FuncOf(add))
}
JavaScriptから呼び出せる関数の1つ目は固定でthis js.Value
でいいで、2つ目はパラメータ関数のパラメータとなります。
足し算の関数なので、パラメータが2つで、配列になっています。
interface{}
はTypeScriptのanyというものだと考えればいいです。
さらにmainの部分を改造
Goのmain関数が受け付けられるように、さらに改造する必要があります。main関数が終了にならないように、クローズしないチャネルをオープンします。
package main
import "syscall/js"
// JS用に改造
func add(this js.Value, inputs []js.Value) interface{} {
return inputs[0].Float() + inputs[1].Float()
}
// チャネルオープンfunc main() {
c := make(chan int) js.Global().Set("add", js.FuncOf(add))
<-c}
ビルド
コンパイルサイズを抑えるために、tinygo
を使います。MacOSのインストール方法はこちらです。
brew tap tinygo-org/tools && brew install tinygo
そしてコンパイルして生成されたJavaScriptもコピーします。
tinygo build -target wasm -o main.wasm -no-debug
# 初回目のみ、2回目から上のコンパイルコマンドを実行するだけでOK
cp "$(tinygo env TINYGOROOT)/targets/wasm_exec.js" .
これでGo側の準備は完了しました。
html側
同じフォルダにindex.html
を作りましょう。定型文なのでそのまま貼り付けます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="wasm_exec.js"></script> <script> async function init() { const go = new Go(); const result = await WebAssembly.instantiateStreaming( fetch("main.wasm"), go.importObject, ); go.run(result.instance); console.log(add(2, 4)) } init() </script></head>
<body>
</body>
</html>
ローカルでホスティング
VSCodeのプラグインLive Server
を使ったらとても簡単にできるのでおすすめです。
そして、index.html
を開いて、下のステータスバーから Go Live
を押したらブラウザで確認できます。
URLはhttp://127.0.0.1:5500/index.html
となります。
DevToolsを開いたら実行できたことを確認できるでしょう。
完了
やはりまだちょっと面倒でしょう。一番気になるのはGoのwasmファイルのサイズですね。次回Rustを使って同じことをやってみたいです。