0%

《Golang》初识WebAssembly

WebAssembly是什么

每一种特定的机器结构(比如x86、ARM)都有自己的汇编语言,WebAssembly是设计的一种通用的汇编语言,不依赖于具体的机器结构,它并不直接映射成特定硬件的机器码在真实机器上运行,而是跟Java等大部分语言一样在虚拟机中运行,各大浏览器都要实现执行WebAssembly的虚拟机

WebAssembly处于IR后面,由后端将IR编译成wasm汇编语言,所以各种语言理论上都是可以生成wasm汇编的,只要实现一个后端编译器即可

WebAssembly工作原理

浏览器拿到WebAssembly代码,会直接解码并优化然后放入虚拟机执行了,没有翻译的过程

为什么比js快

  1. 相同的场景下,编译后的WebAssembly比js文件体积小的多,浏览器下载文件的过程就会短很多
  2. WebAssembly不需要解释的过程,js的v8引擎的解释过程是很耗时的
  3. WebAssembly的后端优化阶段比js更简单快速,没有重优化阶段

Golang编译成WebAssembly

bin/wasm/main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main

import (
"fmt"
"syscall/js"
)

func main() {
defer fmt.Println(`go exited`)

consoleLog := js.Global().Get("console").Get("log")
consoleLog.Invoke("Hello wasm!") // 调用js的函数

js.Global().Call("eval", `console.log("hello, wasm!");`) // 调用js的函数

fun := js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if args == nil {
fmt.Println("not enough args")
return nil
}
fmt.Println("test call")
return js.ValueOf(args[0].Int() + args[1].Int())
})
defer fun.Release()
js.Global().Set("test", fun) // 注册函数到js运行时的全局变量中

fmt.Println(`go end, will be stuck`)
select {

}
}

编译成WebAssembly

1
GOARCH=wasm GOOS=js go build -o ./build/ ./bin/wasm/

编译后生成了build/wasm文件

Golang、Js相互调用

新建一个web项目吧,下面是我的目录结构

1
2
3
4
wasm/
├── index.html
├── wasm
└── wasm_exec.js

index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!doctype html>
<html>

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

<body>
<script src="wasm_exec.js"></script>
<script>
const go = new Go();
let mod, inst;
WebAssembly.instantiateStreaming(fetch("wasm"), go.importObject).then((result) => {
mod = result.module;
inst = result.instance;
go.run(inst);
}).catch((err) => {
console.error(err);
});

async function run() {
console.clear();
alert(test(1,5))
}
</script>

<button onClick="run();" id="runButton">Run</button>
</body>

</html>

wasm_exec.js是golang官方提供一个加载文件,copy过来即可

1
cp $GOROOT/misc/wasm/wasm_exec.js ./wasm/

wasm是上一步编译得到的wasm文件

接下来开启静态资源服务器(可以使用golang或者express写一个),这里直接用别人写好的

1
http-server ./wasm/

浏览器打开 http://127.0.0.1:8080/

点击按钮就可以看到调用效果

注意: 如果使用nodejs调用的话,会出现 all goroutines are asleep - deadlock 的问题,原因不明,知道原因的请不吝赐教

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

import fs from 'fs'
import Starter from '@pefish/js-util-starter'
import './wasm_exec'
import TimeUtil from '@pefish/js-util-time';


Starter.startAsync(async () => {
//@ts-ignore
const go = new Go()
const buf = fs.readFileSync(`./wasm/wasm`)
const res = await WebAssembly.instantiate(buf, go.importObject)
go.run(res.instance)

await TimeUtil.sleep(2000)
//@ts-ignore
const result = test(1, 2)
console.log(result)


}, null, false)



微信关注我,及时接收最新技术文章