Emscripten编译WebAssembly在Worker环境下FS模块未定义如何解决?
问题根因
- 你编译时指定
ENVIRONMENT='web, worker'的情况下,Emscripten默认不会将初始化完成的Module对象作为CommonJS模块的导出值返回,你用require('./program.js')拿到的空对象和Emscripten实际运行时使用的Module不是同一个实例,自然没有挂载FS等属性。 - 你对
onRuntimeInitialized的使用逻辑错误:Emscripten初始化时会读取初始化前就已存在的全局/传入的Module对象上的onRuntimeInitialized回调,你在require之后才给空对象挂载这个回调,Emscripten根本读取不到,也不会触发你写的逻辑。你手动调用await Module.onRuntimeInitialized()本质是执行你自己定义的函数,完全没有等待Emscripten初始化完成。
修复方案
1. 调整编译参数
新增-s MODULARIZE=1参数,让Emscripten输出的JS文件导出一个工厂函数,调用该函数会返回Promise,初始化完成后resolve完整的Module实例:
emcc program.bc -o program.js -s USE_ZLIB=1 -s EXPORTED_RUNTIME_METHODS="['FS', 'callMain']" -s ALLOW_MEMORY_GROWTH=1 -s ENVIRONMENT='web, worker' -s MODULARIZE=1
2. 重写胶水代码
直接调用工厂函数拿到初始化完成的Module实例即可:
const createModule = require('./program.js') async function main(input1, input2){ // 调用工厂函数,等待Wasm初始化完成,拿到可用的Module实例 const Module = await createModule() Module.FS.writeFile('first_input.txt', input1) Module.FS.writeFile('second_input.txt', input2) Module.callMain(['-r', 'first_input.txt', '-q', 'second_input.txt', '-o', 'test.out', '-t', '1']) const output = Module.FS.readFile('test.out', { encoding: 'utf8' }) console.log(output) return output } module.exports = main
MODULARIZE模式返回的Promise会自动在运行时初始化完成后resolve,不需要手动处理onRuntimeInitialized回调。
如果你不想修改编译参数,也可以用全局挂载的兼容方案:
// 先在Worker全局作用域定义Module对象,挂载回调 self.Module = { onRuntimeInitialized: () => { // 此处的self.Module就是初始化完成的实例,可直接调用FS、callMain等接口 } } // 再导入Emscripten的JS文件,它会自动复用全局已存在的Module对象 require('./program.js')
该方案维护性低于工厂函数模式,仅作为临时兼容方案使用。
内容的提问来源于stack exchange,提问作者Dianne
相关产品推荐
相关产品推荐

