As I mentioned back in September I’ve recently started a new job. Due to the nature of the work I’m not going to be able to talk about much of it. But when there are things I can I’ll try and write them up here.
One of my first tasks has been to write a Node-RED wrapper around a 3rd party native library. This library provides a 2 way communication channel to a prototyping environment so needs to use threads to keep track of things in both directions and make use of callbacks to pass that information back into the Javascript side. I dug around for some concrete examples of what I was trying and while I found a few things that came close I didn’t find exactly what I was looking for so here is a stripped back version of the node I created to use as a reference for later.
This is the C++ method that is called when a new instance of the native node module is created. It takes an object reference as an argument to be stored away and used as the context for the callback later.
void Test::New(const Nan::FunctionCallbackInfo<v8::Value>& info) { if (info.IsConstructCall()) { // Invoked as constructor: `new MyObject(...)` Test* obj = new Test(); obj->Wrap(info.This()); info.GetReturnValue().Set(info.This()); v8::Local<v8::Object> context = v8::Local<v8::Object>::Cast(info[0]); obj->context.Reset(context); uv_loop_t* loop = uv_default_loop(); uv_async_init(loop, &obj->async, asyncmsg); } else { // Invoked as plain function `MyObject(...)`, turn into construct call. const int argc = 2; v8::Local<v8::Value> argv[argc] = { info[0], info[1] }; v8::Local<v8::Function> cons = Nan::New<v8::Function>(constructor); info.GetReturnValue().Set(Nan::NewInstance(cons,argc,argv).ToLocalChecked()); } }
The object is created like this on the javascript side where the this is the reference to the object to be used as the context:
function Native() { events.EventEmitter.call(this); //passes "this" to the C++ side for callback this._native = new native.Test(this); }
We then make the callback from C++ here:
void Test::asyncmsg(uv_async_t* handle) { Nan::HandleScope scope; //retrieve the context object Test* obj = (Test*)((callbackData*)handle->data)->context; v8::Local<v8::Object> context = Nan::New(obj->context); //create object to pass back to javascript with result v8::Local<v8::Object> response = Nan::New<v8::Object>(); response->Set(Nan::New<v8::String>("counter").ToLocalChecked(), Nan::New(((callbackData*)handle->data)->counter)); v8::Local<v8::Value> argv[] = { response }; ((Nan::Callback*)((callbackData*)handle->data)->cb)->Call(context,1,argv); free(handle->data); }
Which ends up back on the javascript side of the house here:
Native.prototype._status = function(status) { this.emit("loop", status.counter); }
I’ve uploaded code to githib here if you want to have look at the whole stack and possibly use it as a base for your project.