Node.js与C++:napi调用JavaScript回调函数
liebian365 2024-11-12 13:08 24 浏览 0 评论
前情提要 : 前段时间想要为一个基于 Electron 框架开发的应用编写一个原生模块,以便它能够调用 Win32 API。在这个过程中,需要处理涉及到 JavaScript 函数回调的问题,由于接触 Node.js 的时间比较少,还无法理解其中的精髓,因此被这个问题困扰了很久。
介绍
NAPI(Node-API)是用于构建 Node.js 原生插件的一种API,旨在提供一个稳定的、跨 Node.js 版本的应用程序二进制接口(ABI),使得开发人员可以编写一次插件代码,并且能够在不同版本的 Node.js 上运行而无需重新编译。它的目标是提供一致的接口,使得编写 Node.js 扩展更加容易。
node-addon-api 是一个仅包含头文件的 C++ 包装类,它简化了基于 C 的 Node-API 的使用。
(如何开发node模块网上已经有很多教程啦,这里就不多赘述啦orz)
尝试1:Function 类
// 检查参数是否正确
if(!info[0].IsFunction()){
// ……
}
// 获取传入的 JavaScript 回调函数
Napi::Function callback = info[0].As<Napi::Function>();
// ……
// 在某些条件下调用 JavaScript 回调函数
callback[i].Call({});
开始的时候直接尝试使用Function类,Napi::Function 类提供了一些方法,可用于调用在 JavaScript 中创建并传递给插件的函数。
但是在我的代码中,获取传入回调函数的位置和调用回调函数的位置不在同一个函数中,并且这样写在调用的时候,程序闪退,错误信息为
C++
复制代码
Fatal error in V8: v8::HandleScope::CreateHandle() Cannot create a handle without a HandleScope
看了一些文档也没有搞明白该怎么解决,尝试1大失败(流泪猫猫头.jpg)
尝试2:线程安全函数
由于回调的问题一直解决不了,之后想做的很多东西就都被搁置了。在一天摸鱼的时候突然看到:异步线程安全函数调用 和 调用线程安全的函数。
1. napi_create_threadsafe_function
NAPI_EXTERN napi_status
napi_create_threadsafe_function(napi_env env,
napi_value func,
napi_value async_resource,
napi_value async_resource_name,
size_t max_queue_size,
size_t initial_thread_count,
void* thread_finalize_data,
napi_finalize thread_finalize_cb,
void* context,
napi_threadsafe_function_call_js call_js_cb,
napi_threadsafe_function* result);
- [in] env:调用 API 的环境。
- [in] func:调用的 JavaScript 函数。
- [in] async_resource:与将传递给可能的 async_hooks init 钩子 的异步工作关联的可选对象。
- [in] async_resource_name:一个 JavaScript 字符串,用于为 async_hooks API 公开的诊断信息提供的资源类型提供标识符。
- [in] max_queue_size:队列的最大大小。0 为无限制。
- [in] initial_thread_count:初始获取数,即初始线程数,包括将使用此函数的主线程。
- [in] thread_finalize_data:要传递给 thread_finalize_cb 的可选数据。
- [in] thread_finalize_cb:napi_threadsafe_function 被销毁时调用的可选函数。
- [in] context:附加到生成的 napi_threadsafe_function 的可选数据。
- [in] call_js_cb:可选回调调用 JavaScript 函数以响应不同线程上的调用。详情查看napi_threadsafe_function_call_js
- [out] result:异步线程安全的 JavaScript 函数。
(内心OS:顾名思义,创建一个线程安全的函数,虽然目前对这些参数还不是很清楚,但是现在可以先顺着 napi_threadsafe_function_call_js 这条线索走下去~)
2. napi_threadsafe_function_call_js
与异步线程安全函数调用一起使用的函数指针。通过调用 napi_call_function,然后调用 JS 函数。
具体函数必须满足:
typedef void (*napi_threadsafe_function_call_js)(napi_env env,
// 用于 API 调用的环境,或者 NULL。
napi_value js_callback,
// 要调用的 JavaScript 函数,或者 NULL。
void* context,
// 用于创建线程安全函数的可选数据。
void* data);
// 由辅助线程创建的数据。回调负责将此原生数据转换为 JavaScript 值(使用 Node-API 函数),这些值可以在调用 js_callback 时作为参数传递。
(内心OS:感觉这个函数像是具体JS回调函数的执行者,那怎么调用回调函数捏)
3.napi_call_threadsafe_function
可以在任何调用JS回调函数的地方调用这个函数。
NAPI_EXTERN napi_status
napi_call_threadsafe_function(napi_threadsafe_function func,
// 要调用的异步线程安全 JavaScript 函数。
void* data,
// 通过创建线程安全 JavaScript 函数期间提供的回调 call_js_cb 发送到 JavaScript 的数据。
napi_threadsafe_function_call_mode is_blocking);
// 标志,其值可以是 napi_tsfn_blocking 或 napi_tsfn_nonblocking。
4. 照猫画虎
大概看了一下文档,就开始照猫画虎写自己的代码啦!!
- 创建JS回调函数。(在我的代码中,我的CallbackInfo是一组Object。)
// ……
// 判断参数类型
if(obj.Get("click").IsFunction()){
//创建回调JS函数
//callback_map_[i] = obj.Get("click").As<Function>();(这里是原本使用 Function 类的写法)
napi_value js_callback;
napi_status status;
napi_value srcCbName;
js_callback = obj.Get("click");
status = napi_create_threadsafe_function(env,
js_callback, // napi_value 回调函数
nullptr,
srcCbName,
0,
1,
nullptr,
nullptr,
nullptr,
ThreadSafe_Func_CallJs,
// 实际的执行单元, 在内部调用napi_call_function
&callback_map_[i]);
// 全局threadsafe函数,存在 using CallbackMap = std::map<int, napi_threadsafe_function> 中
if (status == napi_ok) {
// 成功
}
}
- 创建napi_threadsafe_function_call_js
void ThreadSafe_Func_CallJs(napi_env env, napi_value js_callback, void* context, void* data) {
napi_status status;
napi_value undefined;
status = napi_get_undefined(env, &undefined);
status = napi_call_function(env, undefined, js_callback, 0, nullptr, nullptr);
if (status != napi_ok) {
// ……
}
}
- 调用JS回调函数
napi_call_threadsafe_function(callback_map_[wmId], nullptr, napi_tsfn_nonblocking);
- 在JS中调用原生模块
TaskbarThumbnailManager.setThumbarButtons([
{
icon: "Cursor_Laser_Pointer.ico",
tooltip: "上一首",
click() {
console.log('shangyishou\n');
}
}
// ……
]);
总结
回调成功的时候简直忍不住在房间里旋转跳跃不停歇hhh
但是这次成功是有点运气在的,因为还是有很多没有解决的问题orz
仅作个人纪录,感谢所有写过相关文章的其他作者!!
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)