グローバルナビゲーションへ

本文へ

フッターへ

お役立ち情報Blog



Go WebAssemblyでブラウザ上でGoを実行する その3 Javascript側からGoの関数を呼び出す

前回の記事「Go WebAssemblyでブラウザ上でGoを実行する その2 Go側からJavascriptを操作する」では、Go側からJavascriptを操作してみました。

今回はJavascript側からGoの関数を呼び出してみようと思います。

Goでメソッドを定義する

簡単な例として以下を定義しました。

package main

import "fmt"

type Money int

func (m *Money) Add(v Money) {
	*m += v
}

func (m *Money) Print() {
	fmt.Println(*m)
}

func main() {
	m := Money(100)
	m.Add(Money(200))
	m.Print()
}

実行してみるとこんな感じです。

$ go run main.go 
300

ここで定義したAddとPrintをjs側から呼べるようにしていきます。

js側でGoのメソッドが呼び出せるようにWebAssembly化する

package main

import (
	"fmt"
	"syscall/js"
)

type Money int

// wasmから呼び出す為に引数をと返り値を変更している
// 引数 this js.Value, args []js.Value
// 返り値 any
func (m *Money) Add(this js.Value, args []js.Value) any {
	// 引数の値を直接使えないので整数型に変換
	v := Money(args[0].Int())
	*m += v
	return nil
}

func (m *Money) Print(this js.Value, args []js.Value) any {
	fmt.Println(*m)
	return nil
}

func main() {
	c := make(chan struct{}, 0)
	m := Money(100)

	// メソッドをjavascript側に登録する処理
	js.Global().Set("money", js.ValueOf(
		map[string]any{
			"Print": js.FuncOf(m.Print),
			"Add":   js.FuncOf(m.Add),
		},
	))

	// main関数が終了しないようにチャネルを使って処理をブロック
	<-c
}

このファイルをwasmでビルドします。

$ GOOS=js GOARCH=wasm go build -o money.wasm

HTMLに埋め込んで実際に動かしてみる

ビルドしたmoney.wasmとwasm_exec.js、index.htmlを1つのディレクトリにまとめます。

docs$ ls
index.html  money.wasm  wasm_exec.js

index.htmlの内容は以下になります。

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Go wasm sample 3</title>
    <script src="wasm_exec.js"></script>
</head>

<body>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("money.wasm"), go.importObject).then((result) => {
            go.run(result.instance);
        });
    </script>
    <button onClick="money.print();" id="runButton" >print</button><br>
    <button onClick="money.add(100)" id="runButton">add</button><br>
</body>
</html>

この状態で1つ上の階層でサーバを作成して起動させます。

main.go

package main

import (
	"log"
	"net/http"
)

func main() {
	port := "8080"
	http.Handle("/", http.FileServer(http.Dir("./docs/")))
	log.Printf("Listen on port: %s", port)
	http.ListenAndServe(":"+port, nil)
}

サーバ起動

$ go run main.go 
2022/08/17 10:22:38 Listen on port: 8080

挙動確認

ブラウザにアクセスして挙動を確認します。

以下はprintを1回、addを2回、printを1回クリックした状態です。

Javascirpt側からGoのメソッドが呼び出せました!

さいごに

比較的簡単にJavascript側からGoのメソッドを呼び出すことができました。 Go側で圧縮や変換処理など重めな処理を受け持つ場合など有効なのではないでしょうか? ただ、メソッドの引数と返り値をわざわざ変換しないといけないのでテストを書く際に 工夫しないと若干面倒な気もしますね。。

まだまだGoのWebAssemblyはバイナリの大きさなど課題がある状態のようですが、 ポータビリティも高く、場合によってはかなり有用なケースもあると思います。 今後も動向に注目したいと思います。

この記事を書いた人

アーティス
アーティス
創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この記事のカテゴリ

FOLLOW US

最新の情報をお届けします