Golang与WebAssembly中syscall/js的问题探讨

Golang与WebAssembly中syscall/js的问题探讨

使用Go和WebAssembly时遇到的syscall/js问题

我正在研究WebAssembly,觉得Go比Rust更有意思,但还没走多远就遇到了一个大问题。

似乎由于某种原因我无法使用syscall/js。我第一个简单的hello world示例运行得很好,其他Go项目也能正常运行,所以我很确定我的环境没问题。但一旦我尝试使用js对象,一切就都崩溃了。

我的HTML页面如下:

<html>
    <head>
        <meta charset="utf-8">
        <title>Go WebAssembly Demo</title>
    </head>

    <body>
        <script src="wasm_exec.js"></script>
        <script>
		if (!WebAssembly.instantiateStreaming) { // polyfill
			WebAssembly.instantiateStreaming = async (resp, importObject) => {
				const source = await (await resp).arrayBuffer();
				return await WebAssembly.instantiate(source, importObject);
			};
		}

		const go = new Go();
		let mod, inst;

		WebAssembly.instantiateStreaming(fetch("lib.wasm"), go.importObject).then(async (result) => {
			mod = result.module;
			inst = result.instance;
			await go.run(inst)
		});
        </script>
    </body>
</html>

我的Go代码如下:

package main

import "syscall/js"

// import "syscall/js"

func add(i []js.Value) {
	js.Global().Set("output", js.ValueOf(i[0].Int()+i[1].Int()))
	println(js.ValueOf(i[0].Int() + i[1].Int()).String())
}

func subtract(i []js.Value) {
	js.Global().Set("output", js.ValueOf(i[0].Int()-i[1].Int()))
	println(js.ValueOf(i[0].Int() - i[1].Int()).String())
}

func registerCallbacks() {
	js.Global().Set("add", js.NewCallback(add))
	js.Global().Set("subtract", js.NewCallback(subtract))
}

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

	println("WASM Go Initialized")

	registerCallbacks()
	<-c
}

我使用这个命令构建代码:

GOARCH=wasm GOOS=js go build -o lib.wasm main.go

在浏览器中使用syscall/js时出现的错误是:

Unhandled Promise Rejection: TypeError: this._inst.exports.getsp is not a function. (In 'this._inst.exports.getsp()', 'this._inst.exports.getsp' is undefined)

我没有遇到任何编译错误,go build也为我生成了lib.wasm文件。所有不包含syscall/js的代码都能正常工作。我在这里做错了什么?


更多关于Golang与WebAssembly中syscall/js的问题探讨的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

按照您指出的方法替换 Wasm_exec.js 文件后,问题就解决了。我猜从教程下载的那个文件不起作用,而且我完全不知道它其实已经自带了。天啊,我完全找错了方向,之前还一直以为是 syscall/js 的问题。

感谢!

更多关于Golang与WebAssembly中syscall/js的问题探讨的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


你好 Jonas,

我最近关于 WebAssembly 的帖子包含了我 GitHub 上两个可运行示例的代码链接。这会向你展示如何入门。

看起来你正在使用 TutorialEdge 的 WebAssembly “计算器” 示例。(https://tutorialedge.net/golang/go-webassembly-tutorial/)我和论坛上的其他一些人尝试过这个示例,它对我们有效。(除了一个小问题。)

我不能完全确定,但看起来可能是你的 JavaScript 胶水代码有问题。我的处理方式是复制 $GOROOT/misc/wasm 中的 wasm_exec.js 文件。那里还有一个 HTML 文件,展示了如何在你的 HTML 页面中包含该文件和一些额外的 JavaScript 胶水代码(你在第一个代码清单中展示了这部分内容)。我使用了最新 Go 发行版(1.11.2)中的 HTML,并做了两处修改:

  1. WebAssembly.instantiateStreaming(fetch(<wasm_file>) ...) 调用中将 wasm 文件名改为 lib.wasm
  2. 删除这一行:
document.getElementById("runButton").disabled = false;

无论如何,这就是我的做法。我不知道这是否完全正确,因为正如我报告的那样,我在某些操作系统/浏览器组合上遇到了问题。但它几乎总是有效的。这是我目前能提供的最佳方案,我正在等待是否会有更多回复,然后再去 Go 邮件列表提问或提交问题报告。

问题出现在使用了已弃用的 js.NewCallback 函数,这在较新版本的Go中会导致运行时错误。从Go 1.16开始,syscall/js 包中的回调机制发生了变化。

以下是修复后的代码:

package main

import "syscall/js"

func add(this js.Value, args []js.Value) interface{} {
    result := args[0].Int() + args[1].Int()
    js.Global().Set("output", js.ValueOf(result))
    println(js.ValueOf(result).String())
    return nil
}

func subtract(this js.Value, args []js.Value) interface{} {
    result := args[0].Int() - args[1].Int()
    js.Global().Set("output", js.ValueOf(result))
    println(js.ValueOf(result).String())
    return nil
}

func registerCallbacks() {
    js.Global().Set("add", js.FuncOf(add))
    js.Global().Set("subtract", js.FuncOf(subtract))
}

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

    println("WASM Go Initialized")

    registerCallbacks()
    <-c
}

主要修改:

  1. 使用 js.FuncOf 替代 js.NewCallback
  2. 回调函数签名改为 func(this js.Value, args []js.Value) interface{}
  3. 函数返回 interface{}(通常返回 nil

构建命令保持不变:

GOARCH=wasm GOOS=js go build -o lib.wasm main.go

在JavaScript中调用这些函数:

// 在浏览器控制台中测试
add(5, 3)  // 输出 8
subtract(10, 4)  // 输出 6

这个修改后的版本应该能解决 getsp is not a function 错误,因为使用了当前Go版本支持的函数包装器机制。

回到顶部