You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go编译Wasm在Node.js调用遇错:函数调用与主函数结果获取

Go编译Wasm后在Node.js中的调用问题

问题背景

将以下Go代码编译为xxx.wasm后,可执行文件能正常运行,但部分机器导入该包时编译失败。同时在Node.js中调用Wasm时存在两个核心需求:

  1. 如何调用接收字符串数组参数的GetCalculations函数;
  2. 如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:02:43