从nodejs插件发送事件到JavaScript

我目前正在build立一个应用程序(电子),我需要连接一个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?)。 我认为最大的问题是,现在大部分的v8function都需要隔离,而且在整个networking中发现的大多数例子都非常老旧。

在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 不是正确的方法来触发callback,请参阅https://github.com/nodejs/nan/issues/284

从Node.js C ++插件模块发出事件的常见做法是将C ++组件封装为一个javascript组件,并将它们作为单个模块一起发送。 javascript组件将callback传递给c ++组件。 当C ++组件接收到一个事件时,它应该调用JavaScriptcallbackEventEmitter ,而这个callbackEventEmitter应该使用EventEmitteremit事件。

如果你想在实践中看到这个例子,我在我的node-dvbtee插件模块中这样做: http : node-dvbtee

C ++插件源是从src/目录构build的。

JavaScript包装器可以在lib/目录中find。

lib/parser.js里面查找函数listenTables的参数,并用callback函数调用它。 根据模块configuration的options ,该callback将调用self.push() (用于stream式传输)或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组件将不得不将提供的callback函数存储在持久句柄中。 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 。 你实际上可以在你的本地插件的事件生成函数中调用这个callback,如下所示:

 v8::Local<v8::Value> argv[] = { a, b, c }; m_cb.Call(3, argv); 

其中a,b和c的types是v8::Local<v8::Value> 。 当然, argv数组可以有任何大小。 每个元素对应于提供给给定callback函数的参数。