百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

Node.js与C++:napi调用JavaScript回调函数

myzbx 2025-06-24 15:27 27 浏览

前情提要 : 前段时间想要为一个基于 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

仅作个人纪录,感谢所有写过相关文章的其他作者!!

相关推荐

如何设计一个优秀的电子商务产品详情页

加入人人都是产品经理【起点学院】产品经理实战训练营,BAT产品总监手把手带你学产品电子商务网站的产品详情页面无疑是设计师和开发人员关注的最重要的网页之一。产品详情页面是客户作出“加入购物车”决定的页面...

怎么在JS中使用Ajax进行异步请求?

大家好,今天我来分享一项JavaScript的实战技巧,即如何在JS中使用Ajax进行异步请求,让你的网页速度瞬间提升。Ajax是一种在不刷新整个网页的情况下与服务器进行数据交互的技术,可以实现异步加...

中小企业如何组建,管理团队_中小企业应当如何开展组织结构设计变革

前言写了太多关于产品的东西觉得应该换换口味.从码农到架构师,从前端到平面再到UI、UE,最后走向了产品这条不归路,其实以前一直再给你们讲.产品经理跟项目经理区别没有特别大,两个岗位之间有很...

前端监控 SDK 开发分享_前端监控系统 开源

一、前言随着前端的发展和被重视,慢慢的行业内对于前端监控系统的重视程度也在增加。这里不对为什么需要监控再做解释。那我们先直接说说需求。对于中小型公司来说,可以直接使用三方的监控,比如自己搭建一套免费的...

Ajax 会被 fetch 取代吗?Axios 怎么办?

大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!今天给大家带来的主题是ajax、fetch...

前端面试题《AJAX》_前端面试ajax考点汇总

1.什么是ajax?ajax作用是什么?AJAX=异步JavaScript和XML。AJAX是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,AJAX可以使网页实...

Ajax 详细介绍_ajax

1、ajax是什么?asynchronousjavascriptandxml:异步的javascript和xml。ajax是用来改善用户体验的一种技术,其本质是利用浏览器内置的一个特殊的...

6款可替代dreamweaver的工具_替代powerdesigner的工具

dreamweaver对一个web前端工作者来说,再熟悉不过了,像我07年接触web前端开发就是用的dreamweaver,一直用到现在,身边的朋友有跟我推荐过各种更好用的可替代dreamweaver...

我敢保证,全网没有再比这更详细的Java知识点总结了,送你啊

接下来你看到的将是全网最详细的Java知识点总结,全文分为三大部分:Java基础、Java框架、Java+云数据小编将为大家仔细讲解每大部分里面的详细知识点,别眨眼,从小白到大佬、零基础到精通,你绝...

福斯《死侍》发布新剧照 &quot;小贱贱&quot;韦德被改造前造型曝光

时光网讯福斯出品的科幻片《死侍》今天发布新剧照,其中一张是较为罕见的死侍在被改造之前的剧照,其余两张剧照都是死侍在执行任务中的状态。据外媒推测,片方此时发布剧照,预计是为了给不久之后影片发布首款正式预...

2021年超详细的java学习路线总结—纯干货分享

本文整理了java开发的学习路线和相关的学习资源,非常适合零基础入门java的同学,希望大家在学习的时候,能够节省时间。纯干货,良心推荐!第一阶段:Java基础重点知识点:数据类型、核心语法、面向对象...

不用海淘,真黑五来到你身边:亚马逊15件热卖爆款推荐!

Fujifilm富士instaxMini8小黄人拍立得相机(黄色/蓝色)扫二维码进入购物页面黑五是入手一个轻巧可爱的拍立得相机的好时机,此款是mini8的小黄人特别版,除了颜色涂装成小黄人...

2025 年 Python 爬虫四大前沿技术:从异步到 AI

作为互联网大厂的后端Python爬虫开发,你是否也曾遇到过这些痛点:面对海量目标URL,单线程爬虫爬取一周还没完成任务;动态渲染的SPA页面,requests库返回的全是空白代码;好不容易...

最贱超级英雄《死侍》来了!_死侍超燃

死侍Deadpool(2016)导演:蒂姆·米勒编剧:略特·里斯/保罗·沃尼克主演:瑞恩·雷诺兹/莫蕾娜·巴卡林/吉娜·卡拉诺/艾德·斯克林/T·J·米勒类型:动作/...

停止javascript的ajax请求,取消axios请求,取消reactfetch请求

一、Ajax原生里可以通过XMLHttpRequest对象上的abort方法来中断ajax。注意abort方法不能阻止向服务器发送请求,只能停止当前ajax请求。停止javascript的ajax请求...