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

Web Worker中Go-WebAssembly加载前触发onmessage的问题求助

解决Web Worker中Go-WebAssembly加载前触发onmessage的问题

核心问题

主进程初始化Worker后立刻发送消息,但此时WebAssembly还在加载过程中,myGoFunction尚未被Go代码挂载到Worker的全局上下文,导致调用时出现"myGoFunction is not defined"错误。

可行解决方案

方案1:缓存消息队列 + 加载完成后批量处理

在Worker中先将收到的消息缓存到队列,等WASM加载并执行完成后,再逐个处理队列中的消息,既保证消息不丢失,也能避免提前调用未就绪的WASM函数。

修改后的worker.js代码:

importScripts('wasm_exec.js');

const go = new Go();
// 初始化消息缓存队列
const messageQueue = [];
let wasmReady = false;

// 先注册临时的onmessage,缓存未就绪时收到的消息
onmessage = (e) => {
    if (!wasmReady) {
        messageQueue.push(e);
        return;
    }
    // WASM已就绪,直接处理消息
    handleMessage(e);
};

// 加载并运行WASM,完成后标记就绪并处理缓存消息
WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then((result) => {
    go.run(result.instance);
    wasmReady = true;
    // 批量处理缓存的消息
    messageQueue.forEach(msg => handleMessage(msg));
    messageQueue.length = 0;
});

// 抽离消息处理逻辑到单独函数
const handleMessage = (e) => {
    const {settings} = e.data;
    
    if (settings) {
       // 设置参数的逻辑
    } else {
      const someArray = [];
      for (let i= 0; i < 1000000; i++) {
        someArray[i] = calculate(i);
      }
      postMessage({someArray});
    }
};

const calculate = (i) => {
   // 其他业务逻辑
   return myGoFunction(i);
};

方案2:主进程等待WASM就绪信号

让Worker在WASM加载完成后主动向主进程发送"就绪"信号,主进程收到信号后再开始发送任务消息,从根源上避免过早触发消息处理。

修改后的worker.js:

importScripts('wasm_exec.js');

const go = new Go();

WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then((result) => {
    go.run(result.instance);
    // 向主进程发送WASM就绪信号
    postMessage({type: 'wasm-ready'});
    // 此时再注册正式的消息处理函数
    onmessage = handleMessage;
});

const handleMessage = (e) => {
    const {settings} = e.data;
    
    if (settings) {
       // 设置参数的逻辑
    } else {
      const someArray = [];
      for (let i= 0; i < 1000000; i++) {
        someArray[i] = calculate(i);
      }
      postMessage({someArray});
    }
};

const calculate = (i) => {
   // 其他业务逻辑
   return myGoFunction(i);
};

修改后的main.js:

let worker;
const myArray = [/* 你的任务数据集合 */];

const doSomething = () => {
    if (myArray.length > 0) {
        worker.postMessage({ value: myArray.shift() });
    }
};

const init = () => {
    if (worker) worker.terminate();
    worker = new Worker('worker.js');
    
    worker.onmessage = (e) => {
        if (e.data.type === 'wasm-ready') {
            // WASM就绪,先发送初始化设置
            worker.postMessage({ settings: { a: A, b: B, bool: true } });
            // 切换到任务处理的消息回调
            worker.onmessage = (res) => {
                // 处理Worker返回的任务结果
                // ...
                // 继续下一个任务
                doSomething();
            };
            // 启动第一个任务
            doSomething();
        }
    };
};

init();

方案3:用Promise封装WASM加载(避免重复加载)

将WASM加载逻辑封装为一个全局Promise,确保calculate函数调用时WASM已经就绪,同时保证WASM只会被加载一次(解决你之前重复加载导致速度慢的问题)。

修改后的worker.js:

importScripts('wasm_exec.js');

const go = new Go();
// 封装WASM加载为Promise,仅执行一次
const wasmLoadPromise = WebAssembly.instantiateStreaming(fetch("main.wasm"), go.importObject)
.then((result) => {
    go.run(result.instance);
    return true;
});

onmessage = async (e) => {
    // 等待WASM加载完成,后续调用直接复用已完成的Promise状态
    await wasmLoadPromise;
    
    const {settings} = e.data;
    
    if (settings) {
       // 设置参数的逻辑
    } else {
      const someArray = [];
      for (let i= 0; i < 1000000; i++) {
        someArray[i] = calculate(i);
      }
      postMessage({someArray});
    }
};

const calculate = (i) => {
   // 其他业务逻辑
   return myGoFunction(i);
};

总结

  • 方案1适合需要立即接收初始化消息但不急于处理的场景,通过缓存队列保证消息不丢失。
  • 方案2逻辑清晰,主进程与Worker通过信号同步,完全避免过早触发任务处理。
  • 方案3代码最简洁,利用Promise特性天然保证WASM只加载一次,适合大多数业务场景。

内容的提问来源于stack exchange,提问作者insurg3nt3

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 04:20:28