如何在N-API中存储函数引用以便后续调用?
N-API中存储函数引用以便后续调用的正确方式
问题描述
我有一个使用N-API的简单Node.js原生插件,导出两个函数:
set_callback:接收一个函数参数并赋值给全局变量js_callbackcall_callback:使用napi_call_function()调用js_callback
插件使用代码
const addon = require("./build/Release/addon.node"); const callback = function() { console.log("The callback was called!"); }; addon.set_callback(callback); addon.call_callback();
异常现象
在set_callback内部,js_callback是正确的函数类型:
set_callback: js_callback = 00000053268FEE88 typeof(js_callback) = 7 (napi_function)
但在call_callback中,同一个全局变量地址不变,但类型变为napi_object:
call_callback: js_callback = 00000053268FEE88 typeof(js_callback) = 6 (napi_object)
最终导致调用失败:
addon.call_callback(); ^ Error: Invalid argument at Object.<anonymous>
插件源代码
#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)
解决方案
问题根源在于直接将napi_value存储为全局变量——napi_value只是当前N-API环境下的临时引用,无法保证在后续调用中始终有效,Node.js的垃圾回收机制可能会回收该值,或者其内部状态发生变化。
正确的做法是使用N-API的引用管理机制,通过napi_create_reference创建持久引用,napi_delete_reference释放引用,调用时通过napi_get_reference_value获取当前有效的napi_value。
修改后的插件代码
#include <stdio.h> #include <node_api.h> napi_value js_null; // 将全局变量改为存储持久引用,而非直接存储napi_value napi_ref js_callback_ref = NULL; 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)); // 释放之前的引用,避免内存泄漏 if (js_callback_ref != NULL) { napi_delete_reference(env, js_callback_ref); js_callback_ref = NULL; } // 创建持久引用,第三个参数为引用计数(设为1防止被GC回收) NAPI_CALL(env, napi_create_reference(env, argv[0], 1, &js_callback_ref)); printf("set_callback:\n"); napi_valuetype type; NAPI_CALL(env, napi_typeof(env, argv[0], &type)); printf(" callback value = %p\n", argv[0]); printf(" typeof(callback) = %d\n\n", type); return js_null; } napi_value addon_call_callback(napi_env env, napi_callback_info info) { if (js_callback_ref == NULL) { printf("call_callback: No callback set\n"); return js_null; } napi_value js_callback; // 通过持久引用获取当前有效的napi_value NAPI_CALL(env, napi_get_reference_value(env, js_callback_ref, &js_callback)); printf("call_callback:\n"); napi_valuetype type; NAPI_CALL(env, napi_typeof(env, js_callback, &type)); printf(" callback value = %p\n", js_callback); printf(" typeof(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)
关键修改点
- 全局变量从
napi_value js_callback改为napi_ref js_callback_ref,用于存储函数的持久引用 - 在
set_callback中,创建新引用前先释放旧引用,避免内存泄漏;调用napi_create_reference生成持久引用 - 在
call_callback中,通过napi_get_reference_value从持久引用中获取当前有效的函数值,再执行调用
这样就能确保函数引用在后续调用中始终保持有效,不会出现类型变更或调用失败的问题。
内容的提问来源于stack exchange,提问作者user7401478
相关产品推荐
相关产品推荐

