LoginSignup
4

More than 3 years have passed since last update.

Go x WebAssembly ってどんな?

Last updated at Posted at 2019-12-12

この記事は Wano Group Advent Calendar 2019 の12日目の記事となります。

Advent Calendar もそろそろ折り返しですね🎅🎁

GoとWebAssembly

WebAssemblyとは何ぞって話は詳しくはしませんが、ブラウザが読めるアセンブリのコード形式のことです。(詳しくはここ
特徴としてjavascriptに比べ高速という点が挙げられ、jsの補完的な立ち位置で、モダンブラウザは概ねサポートしています。

Goではv1.11よりWebAssemblyがサポートされるようになり、進行形で様々な改善がされているみたいです。
Officialに色々書いてあります。

ということで勉強がてらGo x WebAssemblyをかなり簡単にですが触ってみて、所感を書こうと思います。

Hello Worldしてみる

公式のGetting Started を参考にやってみます。

1. WebAssemblyバイナリ

スクリプトは特に何も意識することないですね。 これをWebAssembly形式にビルドします。
さすがビルド速いです

test.go
package main

import "fmt"

func main() {
    fmt.Println("Hello, WebAssembly!")
}
$ GOOS=js GOARCH=wasm go build -o test.wasm

2. html・jsファイル

すでにGoのインストール時にサンプルがあるのでそれをコピーして使用します。

$ cp /usr/local/Cellar/go/1.12.5/libexec/misc/wasm/wasm_exec.{js,html} ./

3. Webサーバー起動

Webサーバーを立てておきます。

server/server.go
package main

import "net/http"

func main() {
    http.ListenAndServe(":8080", http.FileServer(http.Dir("../")))
}
$ go run server/server.go

4. ブラウザで確認

Run ボタンをクリックすると以下のようにコンソールに出力されます

スクリーンショット 2019-12-09 0.36.30.png

ディレクトリ構成は以下になります。

|--server
| |--server.go
|--test.go
|--test.wasm
|--wasm_exec.html
|--wasm_exec.js

test.wasm を実行するためのスクリプトが wasm_exec.js で、それと test.wasmwasm_exec.html が呼んでいるという流れになります。

JSで簡単な操作をしてみる

👆のファイルを少々手直しして、JSっぽいことをしてみます。

ボタンクリック時に背景色を変更させます。

sample.html
<!doctype html>
<html>

<body>

<script src="wasm_exec.js"></script>
<script>
    const go = new Go();
    let mod, inst;
    WebAssembly.instantiateStreaming(fetch("test.wasm"), go.importObject).then(async (result) => {
        mod = result.module;
        inst = result.instance;

        await go.run(inst);
        inst = await WebAssembly.instantiate(mod, go.importObject); // reset instance
    }).catch((err) => {
        console.error(err);
    });
</script>

<input type="text" id="colorCodeInput">
<button type="submit" id="colorChangeButton">背景色を変更する</button>
</body>

</html>

👇ゴリッゴリにjs叩いてます

sample.go
package main

import (
    "syscall/js"
)

func main() {
    document := js.Global().Get("document")

    body := document.Call("getElementsByTagName", "body").Index(0)
    input := document.Call("getElementById", "colorCodeInput")

    cb := js.FuncOf(func(this js.Value, args []js.Value) interface{}{
        body.Get("style").Set("backgroundColor", input.Get("value").String())
        return nil
    })
    document.Call("getElementById", "colorChangeButton").Call("addEventListener", "click", cb)

    <-make(chan struct{}, 0)
}

こんな感じになります。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3233313932332f63653530633462312d356236652d363462332d653962312d3837313431333330613263332e676966.gif

Goで書いたコードがクライアントサイドで動くのが新鮮✨

画像を使ってみる

canvasを使っていっぱいGopher君を出してみます。

<script> の部分は👆と同じです。

sample.html
<!doctype html>
<html>
    <body>
...
        <canvas width="1500" height="1000" id="sample"></canvas>
    </body>
</html>

sample-2.go
package main

import (
    "math/rand"
    "syscall/js"
    "time"
)

var (
    document = js.Global().Get("document")
    img      = js.Global().Call("eval", "new Image()")
)

func main() {
    img.Set("src", "./image/gopher.png")

    body := document.Call("getElementsByTagName", "body").Index(0)
    body.Call("addEventListener", "mousemove", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
        draw()
        return nil
    }))

    <-make(chan struct{}, 0)
}

func draw() {
    canvas := document.Call("getElementById", "sample")
    ctx := canvas.Call("getContext", "2d")

    rand.Seed(time.Now().UnixNano())
    x := rand.Intn(1000)
    rand.Seed(time.Now().UnixNano() + 100000)
    y := rand.Intn(1000)

    ctx.Call("drawImage", img, x, y)
}

c4iqw-frq4s.gif
* The Go gopher(Gopherくん)は、Renée Frenchによってデザインされました。

このくらいの処理だとJSと体感差はなく、もっと重い画像/動画処理や物理演算などでないとパフォーマンスの差は感じられなそうです。
スマホくらいのcpuだと若干ラグがあったりするかも

まとめ

  • shimmerのような画像処理など実装してみたかったが、使えるパッケージに制約があったりで結構しんどそうだったので今回はここまで

  • コンパイルは早いし、別途インストールする必要もないので非常に手軽

  • サーバー側をGoで書いている場合、サーバー側で定義している値・ロジックなどを使用できて、クライアント側に流出しないのは強みになりそう

  • syscall/js 叩きすぎるとパフォーマンスがもの凄く下がるので、使い方は慣れが必要そう

  • 現状GCがなかったり、デバッグがかなりしづらかったり、必要な場面に遭遇しづらいなどなどありますが、今後Web領域以外での使用や、言語側の改善も含めて追っておいて損はなさそう

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4