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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 22:47:46