Call Go functions from browser JavaScript with WebAssembly

golangwasmjavascript

Pushing the button, the function implemented in Go is called, and it updates the value of text.

the value is updated by the Go function
$ go version
go version go1.18 darwin/amd64

Go can refer variables and call functions in js with syscall/js package. By breaking changes of Go 1.12, NewCallback() is renamed to FuncOf(). The function must return the value that has a type js.ValueOf() expects, so if returns a struct, an error occurs.

package main

import (
	"strconv"
	"syscall/js"
)

func increment(this js.Value, args []js.Value) any {
	counter := js.Global().Get("document").Call("getElementById", "counter")
	counterValue, err := strconv.ParseInt(counter.Get("textContent").String(), 10, 64)
	if err != nil {
		return map[string]any{"error": err.Error()}
	}
	counterValue += int64(args[0].Int())
	counter.Set("textContent", counterValue)
	return map[string]any{"message": counterValue}
}

func main() {
	js.Global().Set("goIncrement", js.FuncOf(increment))
	select {} // keep running
}

Build .wasm file with GOOS=js, GOARCH=wasm. .wasm files in Go are usually large size, this file is also 1.3M.
.wasm files in Go tend to be large. This file is 1.3MB. TinyGo can make it significantly smaller at 165KB.

$ GOOS=js GOARCH=wasm go build -o main.wasm main.go
$ tinygo build -o wasm.wasm -target wasm ./main.go

Load wasm_exec.js that exists in the official repository, and compile and load the .wasm file with WebAssembly.instantiateStreaming.

<html>

<head>
    <script src="wasm_exec.js"></script>
    <script>
        const go = new Go();
        WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject).then((result) => {
            go.run(result.instance);
        });
        function increment(value) {
            ret = goIncrement(value)
            console.log(ret)
        }
    </script>
</head>

<body>
    <div id="counter">0</div>
    <button onClick="increment(1)">+1</button>
</body>

Start a server that delivers these files, and check the operation.

$ npx http-server
$ open localhost:8080/index.html

References

Go WebAssembly Tutorial - Building a Calculator Tutorial | TutorialEdge.net

javascript - Golang’s syscall/js js.NewCallback is undefined - Stack Overflow

How do WebAssembly binaries compiled from different languages compare in size? - Stack Overflow