Node.js原生Addon:异步回调返回对象时Wrap断言错误如何解决?
解决Node.js Addon异步回调中ObjectWrap::Wrap断言失败的问题
嘿,我来帮你搞定这个断言错误!你遇到的(object->InternalFieldCount() > 0)断言失败,核心原因是你直接创建的空v8::Object没有配置内部字段,而ObjectWrap::Wrap()方法要求目标对象必须至少有1个内部字段,用来存储C++类实例的指针。
错误原因详解
你在MyClass::Init()方法里给FunctionTemplate的实例模板设置了SetInternalFieldCount(1),但这只对通过MyClass构造函数创建的实例生效。而你在HandleOKCallback()里直接用Nan::New<v8::Object>()创建的空对象,并没有继承这个配置,所以内部字段数量为0,触发了断言。
正确实现方案
有两种靠谱的解决方式,根据你的需求选择:
方案1:通过MyClass构造函数创建实例(推荐)
这种方式最符合Node.js Addon的规范,因为构造函数已经帮你处理了内部字段的配置和对象绑定。
首先补全MyClass的静态New方法(你之前的代码里没写完):
static void New(const Nan::FunctionCallbackInfo<v8::Value>& info) { if (info.IsConstructCall()) { // 构造调用:直接创建MyClass实例并绑定到v8对象 MyClass* obj = new MyClass(); obj->Wrap(info.This()); info.GetReturnValue().Set(info.This()); } else { // 非构造调用:返回构造函数的实例化结果 const int argc = 0; v8::Local<v8::Value> argv[argc] = {}; v8::Local<v8::Function> cons = Nan::New(constructor); info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); } }
然后修改Worker::HandleOKCallback(),通过构造函数创建实例:
virtual void HandleOKCallback() { // 获取MyClass的构造函数 v8::Local<v8::Function> cons = Nan::New(MyClass::constructor); // 实例化构造函数,得到带有内部字段的v8对象 v8::Local<v8::Object> obj = Nan::NewInstance(cons).ToLocalChecked(); // 回调返回结果 v8::Local<v8::Value> argv[] = { Nan::Undefined(), obj }; callback->Call(2, argv); }
方案2:手动创建带有内部字段的v8对象
如果你需要直接在C++中创建MyClass实例再绑定(比如后台任务中已经创建了实例),可以通过构造函数的实例模板来创建符合要求的v8对象:
virtual void HandleOKCallback() { // 获取MyClass的实例模板(已经配置了内部字段) v8::Local<v8::Function> cons = Nan::New(MyClass::constructor); v8::Local<v8::ObjectTemplate> instanceTpl = cons->GetInstanceTemplate(); // 创建带有内部字段的v8对象 v8::Local<v8::Object> obj = Nan::NewInstance(instanceTpl).ToLocalChecked(); // 创建MyClass实例并绑定到v8对象 MyClass* mc = new MyClass(); mc->Wrap(obj); // 回调返回结果 v8::Local<v8::Value> argv[] = { Nan::Undefined(), obj }; callback->Call(2, argv); }
补全完整的MyClass代码片段
最后把你没写完的部分补全,确保功能完整:
#include <nan.h> #ifndef _WIN32 #include <unistd.h> #define Sleep(x) usleep((x)*1000) #endif class MyClass : public Nan::ObjectWrap { public: class Worker : public Nan::AsyncWorker { public: explicit Worker(Nan::Callback* callback) : Nan::AsyncWorker(callback) {} private: virtual void Execute() {/* 耗时任务 */ Sleep(1000); } virtual void HandleOKCallback() { // 使用方案1的实现 v8::Local<v8::Function> cons = Nan::New(MyClass::constructor); v8::Local<v8::Object> obj = Nan::NewInstance(cons).ToLocalChecked(); v8::Local<v8::Value> argv[] = { Nan::Undefined(), obj }; callback->Call(2, argv); } }; explicit MyClass() : _id(idCtr++) {} ~MyClass() {} static void Init(v8::Local<v8::Object> exports) { v8::Local<v8::FunctionTemplate> tpl = Nan::New<v8::FunctionTemplate>(New); tpl->SetClassName(Nan::New("MyClass").ToLocalChecked()); tpl->InstanceTemplate()->SetInternalFieldCount(1); // 注册原型方法 Nan::SetPrototypeMethod(tpl, "getID", GetID); // 保存构造函数引用 constructor.Reset(tpl->GetFunction()); exports->Set(Nan::New("MyClass").ToLocalChecked(), tpl->GetFunction()); } static void New(const Nan::FunctionCallbackInfo<v8::Value>& info) { if (info.IsConstructCall()) { MyClass* obj = new MyClass(); obj->Wrap(info.This()); info.GetReturnValue().Set(info.This()); } else { const int argc = 0; v8::Local<v8::Value> argv[argc] = {}; v8::Local<v8::Function> cons = Nan::New(constructor); info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); } } static void GetID(const Nan::FunctionCallbackInfo<v8::Value>& info) { MyClass* obj = Nan::ObjectWrap::Unwrap<MyClass>(info.Holder()); info.GetReturnValue().Set(Nan::New(obj->_id)); } static Nan::Persistent<v8::Function> constructor; static int idCtr; int _id; }; // 静态成员初始化 Nan::Persistent<v8::Function> MyClass::constructor; int MyClass::idCtr = 0; // 注册makeObject方法 void MakeObject(const Nan::FunctionCallbackInfo<v8::Value>& info) { Nan::Callback* callback = new Nan::Callback(info[0].As<v8::Function>()); Nan::AsyncQueueWorker(new MyClass::Worker(callback)); } // 模块导出 NODE_MODULE(addon, [](v8::Local<v8::Object> exports) { MyClass::Init(exports); Nan::Set(exports, Nan::New("makeObject").ToLocalChecked(), Nan::New<v8::FunctionTemplate>(MakeObject)->GetFunction()); })
最后验证
这样修改后,你的JS代码调用addon.makeObject()时,回调返回的obj就是正确绑定了MyClass实例的对象,调用obj.getID()就能正常返回ID了。
内容的提问来源于stack exchange,提问作者enobufs
相关产品推荐
相关产品推荐

