Go编译Wasm在Node.js调用遇错:函数调用与主函数结果获取
Go编译Wasm后在Node.js中的调用问题
问题背景
将以下Go代码编译为xxx.wasm后,可执行文件能正常运行,但部分机器导入该包时编译失败。同时在Node.js中调用Wasm时存在两个核心需求:
- 如何调用接收字符串数组参数的
GetCalculations函数; - 如何获取
main函数的执行结果。
对应的Go代码
package main import ( "fmt" "os" "strings" ) func GetCalculations(args []string) string { // 业务逻辑处理args return "some string" } func main() { args := os.Args resultstr := GetCalculations(args) fmt.Println(resultstr) }
遇到的错误场景
场景1:CommonJS方式调用(index.js)
代码:
const fs = require('node:fs'); const wasmBuffer = fs.readFileSync('./xxx.wasm'); WebAssembly.instantiate(wasmBuffer).then(wasmModule => { const { GetCalculations } = wasmModule.instance.exports; const result = GetCalculations([]); console.log(result); console.log(/* 想打印main函数的执行结果 */) });
错误信息:
node:internal/process/promises:289 triggerUncaughtException(err, true /* fromPromise */); ^ [TypeError: WebAssembly.instantiate(): Imports argument must be present and must be an object] Node.js v21.6.1
场景2:ESM方式调用(index.mjs)
执行命令:node --experimental-modules --experimental-wasm-modules ./demos/demos.wasm.mjs
代码:
import { GetCalculations } from '../xxx.wasm'; console.log(GetCalculations(["", ""]))
错误信息:
(node:17464) ExperimentalWarning: Importing WebAssembly modules is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created) node:internal/modules/esm/resolve:844 throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base), null); ^ Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'gojs' imported from C:\Users\ganes\OneDrive\Documents\projects\gouuidjs\gouuid.wasm at packageResolve (node:internal/modules/esm/resolve:844:9) at moduleResolve (node:internal/modules/esm/resolve:901:20) at defaultResolve (node:internal/modules/esm/resolve:1131:11) at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:390:12) at ModuleLoader.resolve (node:internal/modules/esm/loader:359:25) at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:234:38) at ModuleWrap.<anonymous> (node:internal/modules/esm/module_job:85:39) at link (node:internal/modules/esm/module_job:84:36) { code: 'ERR_MODULE_NOT_FOUND' } Node.js v21.6.1
解决方案
前置步骤:准备Go Wasm运行时
Go编译的Wasm依赖官方提供的runtime环境,先复制wasm_exec.js到项目目录:
cp "$(go env GOROOT)/misc/wasm/wasm_exec.js" .
同时用指定目标环境的命令编译Wasm:
GOOS=js GOARCH=wasm go build -o xxx.wasm main.go
1. 正确调用GetCalculations函数(字符串数组参数传递)
Go的Wasm无法直接接收JS数组,需通过Go runtime处理类型转换。以下是可运行的CommonJS代码:
const fs = require('node:fs'); const { Go } = require('./wasm_exec'); async function runWasm() { const go = new Go(); const wasmBuffer = fs.readFileSync('./xxx.wasm'); // 实例化Wasm时必须传入Go runtime的导入对象 const result = await WebAssembly.instantiate(wasmBuffer, go.importObject); // 初始化Go runtime await go.run(result.instance); // 通过全局对象调用导出函数,JS数组会被Go runtime自动转换 const calcResult = globalThis.GetCalculations(["arg1", "arg2", "arg3"]); console.log("GetCalculations结果:", calcResult); } runWasm();
2. 获取main函数的执行结果
main函数执行后会通过fmt.Println输出到stdout,有两种获取方式:
方式1:捕获stdout输出
重写console.log来收集输出内容:
const fs = require('node:fs'); const { Go } = require('./wasm_exec'); async function runWasm() { const go = new Go(); let mainOutput = ''; // 暂存原console.log,用于后续恢复 const originalLog = console.log; // 重写console.log收集输出 console.log = (...args) => { mainOutput += args.join(' ') + '\n'; originalLog(...args); }; const wasmBuffer = fs.readFileSync('./xxx.wasm'); const result = await WebAssembly.instantiate(wasmBuffer, go.importObject); // 运行main函数 await go.run(result.instance); console.log("main函数执行结果:", mainOutput.trim()); // 恢复原console.log console.log = originalLog; } runWasm();
方式2:修改Go代码导出结果
直接在Go代码中把main的结果存入导出变量,避免捕获输出:
package main import ( "fmt" "os" "strings" ) var MainResult string // 导出变量,用于存储main函数的结果 func GetCalculations(args []string) string { return "some string" } func main() { args := os.Args resultstr := GetCalculations(args) MainResult = resultstr // 将结果存入导出变量 fmt.Println(resultstr) }
然后在JS中直接读取该变量:
// ... 前置初始化代码 await go.run(result.instance); console.log("main函数执行结果:", globalThis.MainResult);
ESM方式的修正
Go的Wasm目前不支持直接通过ESM import导入,需使用wasm_exec.js配合WebAssembly.instantiate。以下是ESM版本示例:
import fs from 'node:fs/promises'; import { Go } from './wasm_exec.js'; async function runWasm() { const go = new Go(); const wasmBuffer = await fs.readFile('./xxx.wasm'); const result = await WebAssembly.instantiate(wasmBuffer, go.importObject); await go.run(result.instance); const calcResult = globalThis.GetCalculations(["arg1", "arg2"]); console.log(calcResult); } runWasm();
执行命令:node --experimental-modules index.mjs
内容的提问来源于stack exchange,提问作者Gary
相关产品推荐
相关产品推荐

