Emscripten异步调用冲突求助:混合调用同步C++函数崩溃
解决Emscripten中异步回调内混合异步/同步调用的冲突问题
你遇到的RuntimeError: Aborted(Assertion failed: Cannot have multiple async operations in flight at once)错误,本质是Emscripten单线程运行时模型的限制:不允许同时存在多个未完成的异步操作上下文。具体冲突逻辑如下:
- C++中
func().await()会启动一个同步等待的异步上下文,阻塞等待JS回调执行完成 - JS回调内的
await fetch('./test.txt')会让出线程触发浏览器事件循环,此时Emscripten的异步等待上下文仍处于活跃状态 - 当fetch完成后回到JS回调,调用
Module.sync_func()会试图再次进入Emscripten运行时,但之前的await()上下文未结束,触发断言冲突
以下是几种可靠的解决方案:
方案1:移除C++同步等待,由JS主动通知完成
这是最直观的解决方式,避免C阻塞等待JS异步操作,改为JS在完成所有逻辑后主动调用C的完成通知函数。
修改后的C++代码
#include <emscripten/bind.h> #include <iostream> using namespace std; // 不再等待JS回调完成,直接触发执行 void async_func(emscripten::val func) { func(); } void sync_func() {} // JS回调完成后调用的通知函数 void on_callback_completed() { cout << "JS回调已完成所有操作" << endl; } EMSCRIPTEN_BINDINGS(async_test) { emscripten::function("async_func", &async_func); emscripten::function("sync_func", &sync_func); emscripten::function("on_callback_completed", &on_callback_completed); } int main() { return 0; }
修改后的JS代码
<html> <body> <script src="./out/build/em-x64-debug/async_test.js"></script> <script> async function async_cb(){ await fetch('./test.txt') Module.sync_func() // 通知C++回调已完成 Module.on_callback_completed() } Module['onRuntimeInitialized'] = () => { Module.async_func(async_cb) } </script> </body> </html>
方案2:用Emscripten Promise API替代.await()
如果需要让C++感知JS回调的完成状态,可以使用Emscripten的emscripten_promise_t API异步处理,避免同步等待带来的上下文冲突。
修改后的C++代码
#include <emscripten/bind.h> #include <emscripten/emscripten.h> #include <iostream> using namespace std; void on_promise_resolved() { cout << "JS异步操作已完成" << endl; } void on_promise_rejected() { cerr << "JS异步操作失败" << endl; } void async_func(emscripten::val func) { // 将JS返回的Promise转换为Emscripten Promise对象 emscripten_promise_t promise = func().as<emscripten_promise_t>(); // 注册Promise完成/失败的回调 emscripten_promise_then(promise, EM_ASM_INT({ return function() { Module.on_promise_resolved(); }; }), EM_ASM_INT({ return function() { Module.on_promise_rejected(); }; }), nullptr); } void sync_func() {} EMSCRIPTEN_BINDINGS(async_test) { emscripten::function("async_func", &async_func); emscripten::function("sync_func", &sync_func); emscripten::function("on_promise_resolved", &on_promise_resolved); emscripten::function("on_promise_rejected", &on_promise_rejected); } int main() { return 0; }
JS代码保持原逻辑即可,无需修改。
方案3:JS回调中延迟调用C++同步函数
如果不想修改C代码,可以在JS的await完成后,用微任务延迟调用C同步函数,让Emscripten的异步等待上下文先结束:
修改后的JS回调
async function async_cb(){ await fetch('./test.txt') // 用微任务延迟调用,让Emscripten的await()上下文先结束 queueMicrotask(() => { Module.sync_func() }) }
这种方式适合快速临时修复,但长期来看方案1或2的架构更可靠。
内容的提问来源于stack exchange,提问作者JMRC
相关产品推荐
相关产品推荐

