【问题标题】:Send events from nodejs addon to javascript从nodejs插件发送事件到javascript
【发布时间】:2016-03-30 06:40:31
【问题描述】:

我目前正在构建一个应用程序(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 函数现在都需要一个 Isolate,而且在整个网络上找到的大多数示例都相当陈旧。

Event1 代码在 MakeCallBack 崩溃:

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)

任何帮助将不胜感激!

【问题讨论】:

标签: javascript c++ node.js v8 electron


【解决方案1】:

您不需要单独的v8::Isolate 来从您的代码中调用 V8,如果您希望多个 v8 engines 并行运行多个 Javascript 解释器,您只需要一个。

我认为您的问题的原因是您从另一个线程调用 V8,这是非常危险的。使用uv_acync_t 结构从您的 C++ 插件线程向您的“主/V8”线程发出信号。有关如何执行此操作的更多详细信息,请参阅this thread

此外,MakeCallback不是触发回调的正确方法,请参阅https://github.com/nodejs/nan/issues/284

【讨论】:

  • "emit" 不是回调 - 它是来自 EventEmitter 的方法,只能从 javascript 访问。请参阅我对公认的常见做法的回答...
【解决方案2】:

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

如果您想在实践中看到这样的示例,我在我的 node-dvbtee 插件模块中执行此操作:http://github.com/mkrufky/node-dvbtee

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

可以在lib/ 目录中找到 javascript 包装器。

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++ 插件组件必须将提供的回调函数存储在持久句柄中。 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&lt;v8::Value&gt;。当然,argv 数组可以有任何大小。每个元素对应一个提供给给定回调函数的参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-01
    • 2020-05-29
    • 2016-04-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多