liujiusheng / blog

个人博客,blog
19 stars 0 forks source link

使用Go编写WebAssembly #225

Open liujiusheng opened 2 years ago

liujiusheng commented 2 years ago

现代地图数据量更大,二三维渲染要求更高,必须要借助于WebAssembly提升性能。

GO是一种只需要半天学习就能上手的语言,跨平台编译也极其容易,转换为WebAssembly的方式也极简单,因此应从GO入手高性能WebGIS。

至于怎么把Go的代码转换成WebAssembly,网上的文章经常会写到这类命令:

GOOS=js GOARCH=wasm go build -o ../../json.wasm

对应Windows下用powershell设置的方法为先后执行以下两句:

$env:GOOS="js"

$env:GOARCH="wasm"

编译:

go build -o test.wasm testgoroutine.go

GOOS实际指编译后的代码所运行的操作系统,要在浏览器运行就是js GOARCH指编译后代码要运行的平台,这里是wasm

在命令行中运行:go env可查看GOOS和GOARCH的参数。

在GO安装目录的misc目录下有wasm_exec.js文件。

最简单的调用示例代码:

<!doctype html>
<html>

<head>
    <meta charset="utf-8">
    <title>Go wasm</title>
</head>

<body>
    <script src="wasm_exec.js"></script>
    <script>
        async function run(fileUrl) {
            try {
            const file = await fetch(fileUrl);
            const buffer = await file.arrayBuffer();
            const go = new Go();
            const { instance, module} = await WebAssembly.instantiate(buffer, go.importObject);
            console.log(instance, module);
            go.run(instance);
            } catch (err) {
            console.error(err);
            }
        }
        setTimeout(() => run("./test.wasm"));
    </script>
</body>
</html>

go示例代码:

package main

import (
        "fmt"
        "time"
)

func say(s string) {
        for i := 0; i < 5; i++ {
                time.Sleep(100 * time.Millisecond)
                fmt.Println(s)
        }
}

func main() {
        go say("world")
    say("123")
}

如何将go的函数暴露给js,并通过js传递参数?有几个关键点:

1.go编译器内置有syscall/js这个库,可以用来把go的函数暴露给js,主要通过js.Global().Set("fibFunc", js.FuncOf(fibFunc))

2.通过 args获取参数。

3.计算结果要用js.ValueOf包装后再传回js才能被js使用。

4.js中要先go.run(instance);实例化整个wasm文件内容才能调用其它函数。

go的代码:

package main

import "syscall/js"

// 参考:https://geektutu.com/post/quick-go-wasm.html

func fib(i int) int {
    if i == 0 || i == 1 {
        return 1
    }
    return fib(i-1) + fib(i-2)
}

func fibFunc(this js.Value, args []js.Value) interface{} {
    return js.ValueOf(fib(args[0].Int()))
}

func main() {
    done := make(chan int, 0)
    js.Global().Set("fibFunc", js.FuncOf(fibFunc))
    <-done
}

.html文件的代码:

<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <title>Go wasm</title>
</head>
<body>
    <script src="wasm_exec.js"></script>
    <script>
        async function run(fileUrl) {
            try {
            const file = await fetch(fileUrl);
            const buffer = await file.arrayBuffer();
            const go = new Go();
            const { instance, module} = await WebAssembly.instantiate(buffer, go.importObject);
            console.log(instance, module);
            go.run(instance);
            console.log(fibFunc(4));
            } catch (err) {
            console.error(err);
            }
        }
        setTimeout(() => run("./test.wasm"));
    </script>
</body>
</html>