4

I want my C library to be able to call a JS function multiple times. I got it to work using Nan but am having trouble converting it to N-API/node-addon-api.

How do I save a JS callback function and call it later from C?

Here's what I have using Nan:

Persistent<Function> r_log;
void sendLogMessageToJS(char* msg) {
    if (!r_log.IsEmpty()) {
        Isolate* isolate = Isolate::GetCurrent();
        Local<Function> func = Local<Function>::New(isolate, r_log);
        if (!func.IsEmpty()) {
        const unsigned argc = 1;
        Local<Value> argv[argc] = {
            String::NewFromUtf8(isolate, msg)
        };
        func->Call(Null(isolate), argc, argv);
        }
    }
}
NAN_METHOD(register_logger) {
    Isolate* isolate = info.GetIsolate();
    if (info[0]->IsFunction()) {
        Local<Function> func = Local<Function>::Cast(info[0]);
        Function * ptr = *func;
        r_log.Reset(isolate, func);
        myclibrary_register_logger(sendLogMessageToJS);
    } else {
        r_log.Reset();
    }
}

How do I do the equivalent with node-addon-api? All the examples I've seen immediately call the callback or use AsyncWorker to somehow save the callback. I can't figure out how AsyncWorker is doing it.

ZachB
  • 13,051
  • 4
  • 61
  • 89
iffy
  • 719
  • 1
  • 4
  • 20

1 Answers1

4

I got an answer from the node-addon-api maintainers which led me to this solution:

FunctionReference r_log;
void emitLogInJS(char* msg) {
  if (r_log != nullptr) {
    const Env env = r_log.Env();
    const String message = String::New(env, msg);
    const std::vector<napi_value> args = {message}; 
    r_log.Call(args);
  } 
}
void register_logger(const CallbackInfo& info) {
  r_log = Persistent(info[0].As<Function>());
  myclibrary_register_logger(emitLogInJS);
}
DatGeoudon
  • 342
  • 3
  • 15
iffy
  • 719
  • 1
  • 4
  • 20
  • It does not crash for you with a `# Fatal error in HandleScope::HandleScope / # Entering the V8 API without proper locking in place` when doing `r_log.Call(args)` from a different thread? – Evgeniy Berezovsky Feb 07 '19 at 23:14
  • @EugeneBeresovsky I'm not familiar enough with Node/N-API/C to know if I'm calling it from a different thread or not. I don't explicitly do anything with threads in my C/C++ code or in my JS. All calls to emitLogInJS are originally caused by synchronous JS calls to my synchronous C code. I don't do anything async. – iffy Feb 08 '19 at 05:06
  • Hi @EugeneBeresovsk can you please check https://github.com/nodejs/node-addon-api/issues/947 – Vijay Kumbhani Mar 23 '21 at 12:57
  • @iffy it's leaking memory with large message (eg. 1MB) in time, do u know how to empty the env? – Dee May 13 '22 at 22:51
  • 1
    @Dee I'm sorry, I don't know and have since retired using this code in my project. – iffy Jan 10 '23 at 19:08