I have a simple native Node.js addon that uses N-API.
It exports two functions, set_callback
and call_callback
:
set_callback
takes a function parameter and assigns it to a global variable js_callback
call_callback
calls the js_callback
using napi_call_function()
I'm using the addon like this:
const addon = require("./build/Release/addon.node");
const callback = function() {
console.log("The callback was called!");
};
addon.set_callback(callback);
addon.call_callback();
Inside the set_callback
function, js_callback
actually points to the passed function and it is usable:
set_callback:
js_callback = 00000053268FEE88
typeof(js_callback) = 7 (napi_function)
However, in call_callback
this same global variable now refers to something else: it points to the same value, but now has a different type:
call_callback:
js_callback = 00000053268FEE88
typeof(js_callback) = 6 (napi_object)
Which causes napi_call_function()
to fail:
addon.call_callback();
^
Error: Invalid argument
at Object.<anonymous>
What is the proper way to store a function reference to be able to call it later in N-API?
Source code for the addon:
#include <stdio.h>
#include <node_api.h>
napi_value js_null;
napi_value js_callback;
napi_value addon_set_callback(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1];
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
js_callback = argv[0];
printf("set_callback:\n");
napi_valuetype type;
NAPI_CALL(env, napi_typeof(env, js_callback, &type));
printf(" js_callback = %p\n", js_callback);
printf(" typeof(js_callback) = %d\n\n", type);
return js_null;
}
napi_value addon_call_callback(napi_env env, napi_callback_info info) {
printf("call_callback:\n");
napi_valuetype type;
NAPI_CALL(env, napi_typeof(env, js_callback, &type));
printf(" js_callback = %p\n", js_callback);
printf(" typeof(js_callback) = %d\n\n", type);
napi_value result;
NAPI_CALL(env, napi_call_function(env, js_null, js_callback, 0, NULL, &result));
return js_null;
}
napi_value addon_init(napi_env env, napi_value exports) {
napi_get_null(env, &js_null);
napi_value fn_set_callback;
napi_value fn_call_callback;
NAPI_CALL(env, napi_create_function(env, NULL, 0, addon_set_callback, NULL, &fn_set_callback));
NAPI_CALL(env, napi_create_function(env, NULL, 0, addon_call_callback, NULL, &fn_call_callback));
NAPI_CALL(env, napi_set_named_property(env, exports, "set_callback", fn_set_callback));
NAPI_CALL(env, napi_set_named_property(env, exports, "call_callback", fn_call_callback));
return exports;
}
NAPI_MODULE(NODE_GYP_MODULE_NAME, addon_init)
I'm not a N-API expert, but in V8 terms, what you need is a v8::Persistent
. From a quick look at the N-API documentation, it seems that's called a "reference" there.