将事件从nodejs插件发送到javascript
我目前正在构建一个应用程序(Electron),我需要将它与一个c ++库连接起来。 我已经完成了大部分使用NodeJS c ++插件的绑定,但是我错过了一个与接收由我的Javascript代码上的c ++库生成的事件有关的重要部分。
void Event1(int64_t id)
{
ArrayBufferAllocator allocator;
Isolate::CreateParams create_params;
create_params.array_buffer_allocator = &allocator;
Isolate* isolate = Isolate::New(create_params);
{
v8::Locker locker(isolate);
Isolate::Scope isolate_scope(isolate);
HandleScope handle_scope(isolate);
Local<Context> context = Context::New(isolate);
Context::Scope context_scope(context);
v8::Local<v8::Value> argv[1] = {
Nan::New("OnWillPlayTrack").ToLocalChecked(), // event name
};
Nan::MakeCallback(isolate->GetCurrentContext()->Global(),"emit", 1, argv);
}
isolate->Dispose();
}
Event1由c ++ lib调用,与V8无关,现在我想将一个事件触发到JavaScript,并返回到Node(EventEmitter?)。 我认为最大的问题是,现在大部分v8功能都需要隔离,并且整个网络中发现的大多数示例都很老旧。
在MakeCallBack Event1代码崩溃与:
Thread 24 Crashed:: App
0 libnode.dylib 0x000000010a81e35b v8::String::NewFromOneByte(v8::Isolate*, unsigned char const*, v8::String::NewStringType, int) + 43
1 libnode.dylib 0x000000010a4b042f node::OneByteString(v8::Isolate*, char const*, int) + 15
2 libnode.dylib 0x000000010a4ba1b2 node::MakeCallback(node::Environment*, v8::Local<v8::Object>, char const*, int, v8::Local<v8::Value>*) + 50
3 libnode.dylib 0x000000010a4ba240 node::MakeCallback(v8::Isolate*, v8::Local<v8::Object>, char const*, int, v8::Local<v8::Value>*) + 96
4 addon.node 0x0000000110e62e1f Event1(long long) + 291 (v8.h:6721)
任何帮助将不胜感激!
你不需要单独的v8::Isolate
来从你的代码中调用V8,如果你想让多个v8 engines
同时运行多个Javascript解释器,你只需要一个。
我认为你的问题的原因是你从另一个线程调用V8,这是相当危险的。 使用uv_acync_t
结构来表示你的C ++插件线程中的'main / V8'线程。 有关如何执行此操作的更多详细信息,请参阅此主题。
此外, MakeCallback
不是正确的方法来触发回调,请参阅https://github.com/nodejs/nan/issues/284。
从Node.js C ++插件模块发出事件的常见做法是将C ++组件封装为一个javascript组件,并将它们作为单个模块一起发布。 javascript组件将回调传递给c ++组件。 当C ++组件接收到一个事件时,它应该调用JavaScript回调EventEmitter
,而后者应该使用EventEmitter
来emit
事件。
如果你想在实践中看到这个例子,我在我的node-dvbtee
插件模块中这样做:http: node-dvbtee
C ++插件源是从src/
目录构建的。
javascript包装器可以在lib/
目录中找到。
查看lib/parser.js
,找到函数listenTables
使用回调函数作为参数调用的位置。 根据模块配置的options
,该回调将调用self.push()
(用于流式传输)或self.emit()
来发出事件。
为了避免链接腐烂,javascript示例如下所示:
var _Dvbtee = require('bindings')('dvbtee.node')
var util = require('util')
var stream = require('stream')
var Parser = function (options) {
var self = this
if (!(this instanceof Parser)) {
return new Parser(options)
}
self.options = {}
if (typeof options === 'object') {
self.options.passThru = (options.hasOwnProperty('passThru') && options.passThru)
} else {
self.options.passThru = false
}
var _parser = new _Dvbtee.Parser
var _listenTables = _Dvbtee.Parser.prototype.listenTables
var listenTables = _listenTables.bind(_parser)
stream.Transform.call(this, {
objectMode: !self.options.passThru
})
self._buffer = new Buffer(0)
listenTables(function(a,b,c) {
// arguments a & b are ignored
self.emit('psip', c)
})
}
util.inherits(Parser, stream.Transform)
Parser.prototype._transform = ...
c ++ addon组件必须将提供的回调函数存储在持久句柄中。 NAN为此提供了一个完美的结构: Nan::Callback
c ++插件组件代码应该如下所示:
void dvbteeParser::listenTables(const Nan::FunctionCallbackInfo<v8::Value>& info) {
dvbteeParser* obj = ObjectWrap::Unwrap<dvbteeParser>(info.Holder());
int lastArg = info.Length() - 1;
if ((lastArg >= 0) && (info[lastArg]->IsFunction())) {
obj->m_tableReceiver.subscribe(info[lastArg].As<v8::Function>());
}
info.GetReturnValue().Set(info.Holder());
}
...
void TableReceiver::subscribe(const v8::Local<v8::Function> &fn) {
m_cb.SetFunction(fn);
}
...其中m_cb
是我的TableReceiver
类中的Nan::Callback
。 你可以在你的本地插件的事件生成函数中实际调用这个回调函数,如下所示:
v8::Local<v8::Value> argv[] = {
a,
b,
c
};
m_cb.Call(3, argv);
其中a,b和c的类型为v8::Local<v8::Value>
。 当然, argv
数组可以有任意大小。 每个元素对应于提供给给定回调函数的参数。