TinyGo编译WASM如何返回JS可识别的对象/数组等返回值
你拿到的102752这类数字是WASM线性内存的指针地址,不是实际返回值。WASM核心规范仅支持i32、i64、f32、f64四种数字类型作为函数入参与返回值,所有复杂类型(map、数组、字符串、结构体)都存储在WASM独占的线性内存中,JS无法直接读取,必须手动完成内存交互。
以下是两种可直接落地的实现方案:
方案1:JSON序列化交互(最通用,支持map/数组/结构体)
该方案不需要手动解析Go复杂类型的内部内存布局,兼容性最好,适配所有可序列化的返回类型。
Go侧代码调整
不要直接返回map,将返回值序列化为JSON字节流,写入JS传入的预分配内存中:
package main import ( "encoding/json" "unsafe" ) // 提前定义返回结构,比map[string]interface{}性能更稳定 // 如果要返回数组,直接把res换成[]int{60, 1}即可 type Result struct { Key int `json:"key"` Remove int `json:"remove"` } //export onInput // 入参:JS预分配的内存指针、内存缓冲区大小 // 返回值:写入内存的字节长度,-1代表缓冲区不足 func onInput(bufPtr uintptr, bufSize int) int { res := Result{ Key: 60, Remove: 1, } data, _ := json.Marshal(res) if len(data) > bufSize { return -1 } // 将指针转为字节切片,写入JSON数据 buf := unsafe.Slice((*byte)(unsafe.Pointer(bufPtr)), bufSize) copy(buf, data) return len(data) } func main() {}
编译命令保持原有逻辑即可:
tinygo build -o main.wasm -target wasm ./main.go
JS侧调用逻辑
JS侧先通过TinyGo默认导出的malloc分配WASM内存,调用函数后从内存中读取字节流,解析为JS对象:
const { memory, malloc, free, onInput } = wasm.instance.exports; // 分配1KB缓冲区足够存储当前小对象,大返回值可按需调整大小 const BUF_SIZE = 1024; const bufPtr = malloc(BUF_SIZE); // 调用导出函数,获取写入数据长度 const dataLen = onInput(bufPtr, BUF_SIZE); if (dataLen === -1) { throw new Error("缓冲区不足,无法存储返回值"); } // 从WASM内存读取JSON字符串并解析 const memView = new Uint8Array(memory.buffer, bufPtr, dataLen); const result = JSON.parse(new TextDecoder().decode(memView)); console.log(result); // {key: 60, remove: 1},序列化数组时此处输出[60, 1] // 释放分配的内存,避免泄漏 free(bufPtr);
方案2:固定结构直接传值(适合简单固定结构,性能更高)
如果返回值是固定数量的数值类型,不需要序列化开销,可以通过传入多个指针存储返回值:
Go侧代码
package main //export onInput // 传入两个int类型指针,分别存储两个返回值 func onInput(keyPtr *int, removePtr *int) { *keyPtr = 60 *removePtr = 1 } func main() {}
JS侧代码
const { memory, malloc, free, onInput } = wasm.instance.exports; // TinyGo wasm32目标下int占4字节,分别为两个返回值分配内存 const keyPtr = malloc(4); const removePtr = malloc(4); onInput(keyPtr, removePtr); // 从内存中读取32位整数值 const key = new Int32Array(memory.buffer, keyPtr, 1)[0]; const remove = new Int32Array(memory.buffer, removePtr, 1)[0]; console.log({key, remove}); // {key: 60, remove: 1} free(keyPtr); free(removePtr);
注意事项
- 不要尝试直接在JS侧解析Go原生map、slice的内存布局,TinyGo不同版本的runtime内部结构可能调整,手动解析兼容性极差,JSON序列化是官方推荐的通用交互方式。
- 所有通过
malloc分配的WASM内存,使用完必须调用free释放,否则会造成WASM侧内存泄漏。 - JS向Go传递字符串、数组等复杂参数时逻辑一致:将内容编码为UTF-8字节写入WASM内存,传入指针和长度即可在Go侧读取。
内容的提问来源于stack exchange,提问作者Leone
相关产品推荐
相关产品推荐

