如何 return 通过异步回调在节点本机代码中创建新对象?
How to return a new object created in node native code via asynchronous callback?
我正在尝试创建一个执行以下操作的节点插件:
// js
addon.makeObject((err, obj) => {
if (err) { /* handle error */ }
console.log('New object ID=%d', obj.getID());
:
});
makeObject()
有点像 "asynchronous object factory",其中在后台(使用 Nan::AsyncWorker)的本机 C++ 代码中创建一个新实例,并进行必要的设置,然后返回给 NodeJS通过回调登陆。
这是本机代码的片段:
// cpp
#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:
friend class MyClass;
explicit Worker(Nan::Callback* callback) : Nan::AsyncWorker(callback) {}
private:
virtual void Execute() {/* some long running task */ Sleep(1000); }
virtual void HandleOKCallback() {
MyClass *mc = new MyClass();
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
mc->Wrap(obj); // ==> Assertion failed: (object->InternalFieldCount() > 0)
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(), Nan::GetFunction(tpl).ToLocalChecked());
}
private:
int _id;
static Nan::Persistent<v8::Function> constructor;
static NAN_METHOD(New) {
if (info.IsConstructCall()) {
MyClass* mc = new MyClass();
mc->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<v8::Function>(constructor);
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
}
}
static NAN_METHOD(GetID) {
MyClass *mc = ObjectWrap::Unwrap<MyClass>(info.Holder());
info.GetReturnValue().Set(Nan::New<v8::Integer>(mc->_id));
}
static int idCtr;
};
Nan::Persistent<v8::Function> MyClass::constructor;
int MyClass::idCtr = 0;
NAN_METHOD(MakeObject) {
// Check arguments here...
Nan::Callback *cb = new Nan::Callback(info[0].As<v8::Function>());
Nan::AsyncQueueWorker(new MyClass::Worker(cb)); // starts the worker
info.GetReturnValue().Set(Nan::Undefined());
}
void InitAll(v8::Local<v8::Object> exports) {
exports->Set( Nan::New("makeObject").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(MakeObject)->GetFunction());
MyClass::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
虽然编译成功,但在这一行失败:
mc->Wrap(obj);
这是错误:
Assertion failed: (object->InternalFieldCount() > 0), function Wrap, file ../node_modules/nan/nan_object_wrap.h, line 55.
什么是InternalFieldCount
?我一直在谷歌上寻找方法,但到目前为止没有运气......
我自己找到了答案。关键是在构造函数上使用 Nan::NewInstance - 与将 JS 代码调用构造函数作为普通函数处理的方式相同。
这是完整的有效代码:
// addon.cpp
#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:
friend class MyClass;
explicit Worker(Nan::Callback* callback) : Nan::AsyncWorker(callback) {}
private:
virtual void Execute() {/* some long running task */ Sleep(1000); }
virtual void HandleOKCallback() {
///////////////////////////////////////////////////////////
// Create MyClass instance and pass it to JS via callback
const int argc = 0;
v8::Local<v8::Value> argv[argc] = {};
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> obj = Nan::NewInstance(cons, argc, argv).ToLocalChecked();
v8::Local<v8::Value> _argv[] = { Nan::Undefined(), obj };
callback->Call(2, _argv);
}
};
explicit MyClass() : _id(idCtr++) {}
~MyClass() {}
static NAN_MODULE_INIT(Init) {
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());
target->Set(Nan::New("MyClass").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
}
private:
int _id;
static Nan::Persistent<v8::Function> constructor;
static NAN_METHOD(New) {
if (info.IsConstructCall()) {
MyClass* mc = new MyClass();
mc->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<v8::Function>(constructor);
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
}
}
static NAN_METHOD(GetID) {
MyClass *mc = ObjectWrap::Unwrap<MyClass>(info.Holder());
info.GetReturnValue().Set(Nan::New<v8::Integer>(mc->_id));
}
static int idCtr;
};
Nan::Persistent<v8::Function> MyClass::constructor;
int MyClass::idCtr = 0;
NAN_METHOD(MakeObject) {
// Check arguments here...
Nan::Callback *cb = new Nan::Callback(info[0].As<v8::Function>());
Nan::AsyncQueueWorker(new MyClass::Worker(cb)); // starts the worker
info.GetReturnValue().Set(Nan::Undefined());
}
NAN_MODULE_INIT(InitAll) {
target->Set( Nan::New("makeObject").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(MakeObject)->GetFunction());
MyClass::Init(target);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
我正在尝试创建一个执行以下操作的节点插件:
// js
addon.makeObject((err, obj) => {
if (err) { /* handle error */ }
console.log('New object ID=%d', obj.getID());
:
});
makeObject()
有点像 "asynchronous object factory",其中在后台(使用 Nan::AsyncWorker)的本机 C++ 代码中创建一个新实例,并进行必要的设置,然后返回给 NodeJS通过回调登陆。
这是本机代码的片段:
// cpp
#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:
friend class MyClass;
explicit Worker(Nan::Callback* callback) : Nan::AsyncWorker(callback) {}
private:
virtual void Execute() {/* some long running task */ Sleep(1000); }
virtual void HandleOKCallback() {
MyClass *mc = new MyClass();
v8::Local<v8::Object> obj = Nan::New<v8::Object>();
mc->Wrap(obj); // ==> Assertion failed: (object->InternalFieldCount() > 0)
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(), Nan::GetFunction(tpl).ToLocalChecked());
}
private:
int _id;
static Nan::Persistent<v8::Function> constructor;
static NAN_METHOD(New) {
if (info.IsConstructCall()) {
MyClass* mc = new MyClass();
mc->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<v8::Function>(constructor);
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
}
}
static NAN_METHOD(GetID) {
MyClass *mc = ObjectWrap::Unwrap<MyClass>(info.Holder());
info.GetReturnValue().Set(Nan::New<v8::Integer>(mc->_id));
}
static int idCtr;
};
Nan::Persistent<v8::Function> MyClass::constructor;
int MyClass::idCtr = 0;
NAN_METHOD(MakeObject) {
// Check arguments here...
Nan::Callback *cb = new Nan::Callback(info[0].As<v8::Function>());
Nan::AsyncQueueWorker(new MyClass::Worker(cb)); // starts the worker
info.GetReturnValue().Set(Nan::Undefined());
}
void InitAll(v8::Local<v8::Object> exports) {
exports->Set( Nan::New("makeObject").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(MakeObject)->GetFunction());
MyClass::Init(exports);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)
虽然编译成功,但在这一行失败:
mc->Wrap(obj);
这是错误:
Assertion failed: (object->InternalFieldCount() > 0), function Wrap, file ../node_modules/nan/nan_object_wrap.h, line 55.
什么是InternalFieldCount
?我一直在谷歌上寻找方法,但到目前为止没有运气......
我自己找到了答案。关键是在构造函数上使用 Nan::NewInstance - 与将 JS 代码调用构造函数作为普通函数处理的方式相同。
这是完整的有效代码:
// addon.cpp
#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:
friend class MyClass;
explicit Worker(Nan::Callback* callback) : Nan::AsyncWorker(callback) {}
private:
virtual void Execute() {/* some long running task */ Sleep(1000); }
virtual void HandleOKCallback() {
///////////////////////////////////////////////////////////
// Create MyClass instance and pass it to JS via callback
const int argc = 0;
v8::Local<v8::Value> argv[argc] = {};
v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor);
v8::Local<v8::Object> obj = Nan::NewInstance(cons, argc, argv).ToLocalChecked();
v8::Local<v8::Value> _argv[] = { Nan::Undefined(), obj };
callback->Call(2, _argv);
}
};
explicit MyClass() : _id(idCtr++) {}
~MyClass() {}
static NAN_MODULE_INIT(Init) {
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());
target->Set(Nan::New("MyClass").ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked());
}
private:
int _id;
static Nan::Persistent<v8::Function> constructor;
static NAN_METHOD(New) {
if (info.IsConstructCall()) {
MyClass* mc = new MyClass();
mc->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<v8::Function>(constructor);
info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked());
}
}
static NAN_METHOD(GetID) {
MyClass *mc = ObjectWrap::Unwrap<MyClass>(info.Holder());
info.GetReturnValue().Set(Nan::New<v8::Integer>(mc->_id));
}
static int idCtr;
};
Nan::Persistent<v8::Function> MyClass::constructor;
int MyClass::idCtr = 0;
NAN_METHOD(MakeObject) {
// Check arguments here...
Nan::Callback *cb = new Nan::Callback(info[0].As<v8::Function>());
Nan::AsyncQueueWorker(new MyClass::Worker(cb)); // starts the worker
info.GetReturnValue().Set(Nan::Undefined());
}
NAN_MODULE_INIT(InitAll) {
target->Set( Nan::New("makeObject").ToLocalChecked(),
Nan::New<v8::FunctionTemplate>(MakeObject)->GetFunction());
MyClass::Init(target);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, InitAll)