Go开发WASM时注册供JS调用的函数报undefined错误怎么解决?
问题根因汇总
两个案例的核心问题都和WASM异步加载的执行时序相关,附加少量API调用错误:
1. 标准Go WASM案例问题
原因
你在HTML底部的console.log(sub(5,3))是同步执行的,而WASM的加载、实例化、执行Go main函数的全流程都是异步操作:
loadWasm.js里的init是异步函数,fetch下载WASM文件、WebAssembly.instantiateStreaming实例化、go.run执行Go代码都是异步任务- 当你同步调用
sub时,Go main函数里的js.Global().Set("sub", js.FuncOf(sub))还没执行,全局作用域下自然没有sub函数,触发undefined错误。
修复方案
把调用sub的逻辑推迟到WASM加载完成后执行,修改方式如下:
第一步:修改loadWasm.js,在WASM初始化完成后抛出自定义事件通知页面:
async function init(){ const go = new Go(); const result = await WebAssembly.instantiateStreaming( fetch("http://localhost:8080/www/main.wasm"), go.importObject ); go.run(result.instance); // 抛出加载完成事件 window.dispatchEvent(new CustomEvent('wasmInitDone')); } init();
第二步:修改HTML中的调用逻辑,监听加载完成事件后再调用sub:
<script> window.addEventListener('wasmInitDone', () => { console.log(sub(5,3)); }) </script>
2. TinyGo WASM案例问题
原因
存在三个错误点:
- 时序问题和标准Go案例一致:你在代码末尾同步调用
console.log('multiplied two numbers:', exports.multiply(5, 3));时,WASM还没完成实例化,导出函数还不可用 - 导出函数取值错误:没有声明过
exports变量,TinyGo导出的函数挂载在WebAssembly.instantiate返回的实例obj.instance.exports上 - 资源路径错误:你编译生成的WASM文件是
main2.wasm,但代码里填的WASM_URL = 'main.wasm',会返回404加载失败。
修复方案
修改JS代码如下:
const go = new Go(); // Defined in wasm_exec.js go.importObject.env = { 'main.add': function(x, y) { return x + y } } // 修正WASM路径 const WASM_URL = 'main2.wasm'; var wasm; function callMultiply() { console.log('multiplied two numbers:', wasm.exports.multiply(5, 3)); } if ('instantiateStreaming' in WebAssembly) { WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) { wasm = obj.instance; go.run(wasm); // 实例化完成后再调用导出函数 callMultiply(); }) } else { fetch(WASM_URL).then(resp => resp.arrayBuffer() ).then(bytes => WebAssembly.instantiate(bytes, go.importObject).then(function (obj) { wasm = obj.instance; go.run(wasm); // 实例化完成后再调用导出函数 callMultiply(); }) ) }
额外注意:TinyGo的//export注释和函数定义之间不能有空行,你现有代码的写法是符合要求的。
内容的提问来源于stack exchange,提问作者Hasan A Yousef
相关产品推荐
相关产品推荐

