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

如何在AsyncWorker中正确处理Object/Buffer/Uint8Array?

在AsyncWorker中处理Uint8Array参数的疑问

我在文档和示例中未找到如何在AsyncWorker中正确处理Object/Buffer/Uint8Array参数的方法。我的需求是接收Uint8Array格式的数据并在AsyncWorker中使用,且了解这些数据可能会被垃圾回收,想请教以下问题:

  1. 是否需要将对象用ObjectReference包裹后传入AsyncWorker构造函数?
  2. 是否需要将Uint8Array用ObjectReference包裹?
  3. 是否需要对uint8_t* data进行某种包装?

问题解答

  1. 外层普通对象不需要用ObjectReference包裹。我们实际依赖的是对象内部的Uint8Array,只要保证Uint8Array不被GC回收即可,外层对象的生命周期不影响底层数据的可用性。
  2. 必须用ObjectReference包裹Uint8Array。AsyncWorker的Execute()方法在后台线程异步执行,JS侧的Uint8Array可能在任务完成前被垃圾回收,导致你持有的uint8_t*指针变成野指针,引发崩溃或内存访问错误。通过Napi::ObjectReference持有持久引用,能阻止GC回收该对象,直到你主动释放引用。
  3. 不需要对uint8_t* data做额外包装,但必须确保在AsyncWorker的整个生命周期内,对应的Uint8Array始终存活(通过ObjectReference持有引用)。

AsyncWorker文档核心内容翻译

AsyncWorker用于在后台线程执行耗时任务,避免阻塞Node.js事件循环。关于异步任务中处理JS对象的关键规则:

  • 异步任务执行期间,JS侧的对象可能被垃圾回收,因此如果C++代码需要在Execute()方法中访问JS对象的底层数据,必须通过**持久引用(Persistent Reference)**持有该对象,防止其被GC回收。
  • 持久引用需要在任务完成(无论成功或失败)后主动调用Reset()释放,否则会造成内存泄漏。

修正后的示例代码

C++部分

#include "napi.h"

class DoHeavyMathWorker : public Napi::AsyncWorker {
public:
    DoHeavyMathWorker(const Napi::Env& env, Napi::Uint8Array uint8Array)
        : Napi::AsyncWorker{env, "DoHeavyMathWorker"},
          m_deferred{env},
          m_uint8ArrayRef{Napi::Persistent(uint8Array)},  // 创建Uint8Array的持久引用
          m_data{uint8Array.Data()}
    {}

    ~DoHeavyMathWorker() {
        m_uint8ArrayRef.Reset();  // 析构时确保释放引用
    }

    Napi::Promise GetPromise() { return m_deferred.Promise(); }

protected:
    void Execute() {
        // 安全访问m_data,因为m_uint8ArrayRef持有引用,内存不会被回收
        m_result = (m_data[0] == 0) ? 1 : 2;
    }

    void OnOK() { 
        m_deferred.Resolve(Napi::Number::New(Env(), m_result));
        m_uint8ArrayRef.Reset();  // 任务成功后释放引用
    }
    void OnError(const Napi::Error& err) { 
        m_deferred.Reject(err.Value());
        m_uint8ArrayRef.Reset();  // 任务失败时也要释放引用
    }

private:
    Napi::Promise::Deferred m_deferred;
    Napi::ObjectReference m_uint8ArrayRef;  // 持有Uint8Array的引用,阻止GC回收
    uint8_t* m_data;
    uint32_t m_result;
};

Napi::Value DoHeavyMath(const Napi::CallbackInfo& info) {
    Napi::Env env = info.Env();

    if (!info[0].IsObject()) {
        Napi::TypeError::New(env, "参数必须是对象")
            .ThrowAsJavaScriptException();
        return env.Undefined();
    }
    Napi::Object obj = info[0].As<Napi::Object>();
    if (!obj.Has("data") || !obj.Get("data").IsUint8Array()) {
        Napi::TypeError::New(env, "data必须是Uint8Array类型")
            .ThrowAsJavaScriptException();
        return env.Undefined();
    }
    Napi::Uint8Array uint8Array = obj.Get("data").As<Napi::Uint8Array>();

    DoHeavyMathWorker* worker = new DoHeavyMathWorker(env, uint8Array);
    worker->Queue();
    return worker->GetPromise();
}

Napi::Object Init(Napi::Env env, Napi::Object exports) {
    exports.Set("doHeavyMath", Napi::Function::New(env, DoHeavyMath));
    return exports;
}

NODE_API_MODULE(NODE_GYP_MODULE_NAME, Init)

JS部分

const result = await addon.doHeavyMath({
  data: new Uint8Array([42]),
});
console.log('result:', result);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 12:34:51