2015-12-23 7 views
7

Ich baue gerade eine App (Electron) und ich muss es mit einer C++ - Bibliothek verbinden. Ich habe den größten Teil der Bindung mit den NodeJS-C++ - Addons gemacht, aber mir fehlt ein wichtiger Teil, der mit dem Empfang der von der C++ - Bibliothek erzeugten Ereignisse in meinem Javascript-Code zusammenhängt.Ereignisse von nodejs addon zu Javascript senden

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(); 
} 

Die Event1 wird durch die C++ lib genannt zu werden, die nichts mit V8 zu tun hat, und ich möchte jetzt ein Ereignis JavaScript abzufeuern, zurück zu Knoten (EventEmitter?). Ich denke, dass das größte Problem darin besteht, dass die meisten v8-Funktionen jetzt ein Isolate benötigen, und die meisten Beispiele im Web sind ziemlich alt.

Der Event1 Code stürzt bei MakeCallBack mit:

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) 

Jede Hilfe sehr geschätzt wird!

+0

Dieses _could_ kann ein Duplikat von http://stackoverflow.com/questions/31207454/v8-multithreaded-function sein – ekarak

+0

Dies ist kein Duplikat. Der Zugriff auf die Node.js 'EventEmitter' aus JavaScript innerhalb des C++ - Addon-Bereichs erfordert eine spezielle Prozedur, die ich in meiner Antwort unten erläutert habe. Multithreading innerhalb eines v8 C++ Addons ist ein völlig anderes Problem. – mkrufky

Antwort

2

Sie benötigen keine separate v8::Isolate, um V8 aus Ihrem Code aufzurufen, Sie möchten nur eine, wenn Sie mehrere v8 engines parallel mehrere Javascript-Interpreter ausführen möchten.

Ich denke, der Grund für Ihr Problem ist, dass Sie V8 aus einem anderen Thread aufrufen, was ziemlich riskant ist. Verwenden Sie eine Struktur uv_acync_t, um Ihren Haupt/V8-Thread von Ihrem C++ - Addon-Thread zu signalisieren. Weitere Informationen dazu finden Sie unter this thread.

MakeCallback ist nicht der richtige Weg, um Rückrufe abzufeuern, siehe https://github.com/nodejs/nan/issues/284.

+0

"emit" ist kein Callback - es ist eine Methode aus dem 'EventEmitter', auf die nur über Javascript zugegriffen werden soll. Siehe meine Antwort für die gängige Praxis ... – mkrufky

2

Die allgemeine Praxis zum Ausgeben von Ereignissen aus einem Node.js C++ - Zusatzmodul besteht darin, die C++ - Komponente mit einer JavaScript-Komponente zu umhüllen und sie als einzelnes Modul zusammen zu senden. Die JavaScript-Komponente übergibt einen Callback an die C++ - Komponente. Wenn die C++ - Komponente ein Ereignis empfängt, sollte sie den JavaScript-Rückruf aufrufen, der wiederum das Ereignis EventEmitter bis emit verwenden soll.

Wenn Sie mögen ein Beispiel für den in der Praxis zu sehen, das tue ich dies in meiner node-dvbtee Add-on-Modul: http://github.com/mkrufky/node-dvbtee

Die C++ addon Quelle aus dem src/ Verzeichnis erstellt wird.

Der JavaScript-Wrapper befindet sich im Verzeichnis lib/.

In lib/parser.js suchen, wo die Funktion listenTables mit einer Callback-Funktion als Parameter aufgerufen wird. Abhängig von der konfigurierten options des Moduls ruft dieser Rückruf entweder (für Streaming) oder self.emit() an, um ein Ereignis auszulösen.

Toter Link zu vermeiden, sieht die Javascript-Probe wie folgt aus:

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 = ... 

Die C++ Add-on-Komponente der mitgelieferten Callback-Funktion in einem dauerhaften Griff speichern würde.NAN bietet eine perfekte Struktur für diese:

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); 
} 

... wo m_cb ist: Nan::Callback

Die C++ Add-on-Komponente Code etwa wie folgt aussehen sollte eine Nan::Callback innerhalb meiner TableReceiver Klasse. Sie können tatsächlich diesen Rückruf in Ihrer Mutter wie so Ereignis erzeugende Funktion des addon nennen:

v8::Local<v8::Value> argv[] = { 
    a, 
    b, 
    c 
}; 

m_cb.Call(3, argv); 

wobei a, b und c sind vom Typ v8::Local<v8::Value>. Natürlich kann das argv Array jede Größe haben. Jedes Element entspricht einem Argument, das an die angegebene Rückruffunktion übergeben wird.