NodeJs C/C++ Addons 對(duì)象參數(shù)及回調(diào)函數(shù)

系列文章

  1. C/C++ Addons 入門(mén) Hello world!
  2. C/C++ Addons 對(duì)象參數(shù)及回調(diào)函數(shù)
  3. C/C++ Addons 非阻塞多線(xiàn)程回調(diào)
  4. C/C++ Addons windows 下 .dll 動(dòng)態(tài)鏈接庫(kù) 實(shí)用篇

完整代碼

寫(xiě) node.js C/C++ 插件目的就是為了擴(kuò)展一些 node.js 自身不具備的功能,在 node.js 中有些模塊自身就是 C/C++ 寫(xiě)的,和 C/C++ 插件可以說(shuō)很像的東西了,如: Buffer crypro evals fs http os zlib tcp ddp 等(參考深入淺出nodejs)。

  • 拿我們常用的 http 來(lái)舉例,如果你想讀一個(gè)請(qǐng)求一段網(wǎng)絡(luò)數(shù)據(jù) (node.js官方例子):
const http = require('http');
const postData = querystring.stringify({
  'msg': 'Hello World!'
});

const options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Content-Length': Buffer.byteLength(postData)
  }
};

const req = http.request(options, (res) => {
  console.log(`STATUS: ${res.statusCode}`);
  console.log(`HEADERS: ${JSON.stringify(res.headers)}`);
  res.setEncoding('utf8');
  res.on('data', (chunk) => {
    console.log(`BODY: ${chunk}`);
  });
  res.on('end', () => {
    console.log('No more data in response.');
  });
});

req.on('error', (e) => {
  console.error(`problem with request: ${e.message}`);
});

// Write data to request body
req.write(postData);
req.end();

可以看出來(lái),http.request 第一個(gè)參數(shù)是 Object 第二個(gè)參數(shù)是 function,那么我們也試試寫(xiě)一個(gè)支持同樣參數(shù)的 C/C++ Addons 出來(lái)試試吧 ??

  • C/C++ 代碼 src/args-callback.c
#include <stdio.h>
#include <node_api.h>
#include <assert.h>

static void call_js_fn(napi_env env, napi_callback_info info) {
    size_t argc = 2;     // 入?yún)€(gè)數(shù)
    napi_value args[2];  // 入?yún)?shù)組
    assert(napi_get_cb_info(env, info, &argc, args, NULL, NULL) == napi_ok);

    napi_value json = args[0]; // 第一個(gè)參數(shù) json 對(duì)象
    napi_value name_key, name; // json 中的 name
    napi_value age_key, age;   // json 中的 age
    napi_value cb = args[1];   // 第二個(gè)參數(shù) function 回調(diào)

    // 將 C 語(yǔ)言的 char* name、age 變成 napi 的 string
    // 用于 napi 提供的一些列 API,如 napi_get_property
    napi_create_string_utf8(env, "name", NAPI_AUTO_LENGTH, &name_key);
    napi_create_string_utf8(env, "age", NAPI_AUTO_LENGTH, &age_key);

    napi_get_property(env, json, name_key, &name); // 取出 json 中的 name
    napi_get_property(env, json, age_key, &age);   // 取出 json 中的 age
    
    napi_value argv[] = {name, age}; // 調(diào)用 js 回調(diào)時(shí)候傳入的參數(shù)
    napi_value global;
    napi_get_global(env, &global);   // 獲取當(dāng)前執(zhí)行 js 的 global 對(duì)象
    napi_value result;

    napi_call_function( // 調(diào)用 js 回調(diào)函數(shù)
        env,    // 當(dāng)前程序執(zhí)行上下文
        global, // js 回調(diào)的 this 對(duì)象,在 js 回調(diào)中可以驗(yàn)證: console.log(this === global); // true
        cb,     // js 回調(diào)函數(shù)句柄
        2,      // js 回調(diào)函數(shù)接受參數(shù)個(gè)數(shù)
        argv,   // js 回調(diào)函數(shù)參數(shù)數(shù)組
        &result // js 回調(diào)函數(shù)中如果有 retrun,將會(huì)被 result 接受到
    );
}

/** Addons 入口 */
napi_value Init(napi_env env, napi_value exports) {
    // 以 function 形式導(dǎo)出
    // 使用:addon(args);
    napi_value new_exports;
    napi_status status = napi_create_function(env,
        "",                // function 名字(fn.name)
        NAPI_AUTO_LENGTH,  // 應(yīng)該是(fn.length)
        call_js_fn,        // function 句柄
        NULL,              // 個(gè)人理解應(yīng)該是 call 或者 applay 提供的第一個(gè)參數(shù),歡迎大神補(bǔ)充 ??
        &new_exports);
    assert(status == napi_ok);
    return new_exports;

    /* 以對(duì)象的格式導(dǎo)出
     * 使用:addon.call(args);
    napi_value obj;
    napi_create_object(env, &obj);
    napi_property_descriptor desc = {
        "call",
        NULL,
        call_js_fn,
        NULL,
        NULL,
        NULL,
        napi_default,
        NULL };
    napi_define_properties(env, obj, 1, &desc);
    return obj;*/
    // 可以理解為 exports == obj,所以導(dǎo)出用自定義的 obj 和注入的 exports 都可以的 :)
    // napi_define_properties(env, exports, 1, &desc);
    // return exports;
}


NAPI_MODULE(NODE_GYP_MODULE_NAME, Init)

  • javascript test/args-callback.js
const addon = require('../build/Release/args_callback.node');

const arg = { name: 'anan', age: 29 };
const cb = function (name, age) {
  console.log(`The name is ${name}`, `\nAge is ${age}`);
  console.log(this === global); // true
};

addon(arg, cb); // 如果導(dǎo)出的是 function
// addon.call(arg, cb); // 如果導(dǎo)出的是 exports 對(duì)象,并且 call 掛載在 exports 上
  • 運(yùn)行一下 完美
$ node test/args-callback.js
The name is anan
Age is 29
true

乍一看來(lái)我們 “實(shí)現(xiàn)了” 對(duì)象參數(shù)及回調(diào)函數(shù) 了呢 ??,都知道 http.request 是異步的,所以不會(huì)阻塞 js 線(xiàn)程,那么我們?cè)囋嚹M下 異步

  • C/C++ 改造
// windows.h 里面提供了 Sleep
#include <windows.h>
...
// 模擬等待 1 秒后執(zhí)行
Sleep(1000);
napi_call_function(...);
  • javascript 改造
// 0.1 秒之后執(zhí)行
setTimeout(() => console.log('Timeout is executed.'), 100);
// 調(diào)用 Addons
addon({ name: 'anan', age: 29 }, function (name, age) {...}); 

按我們一貫使用 node.js 異步的思維,這段程序應(yīng)該會(huì)先輸出 Timeout is executed. 然后再輸出 name, age的值

  • 運(yùn)行一下
$ node test/args-callback.js
The name is anan
Age is 29
true
Timeout is executed.

哎嗎呀!打臉了 ??,為啥子會(huì)醬紫呢?
其實(shí) Addons 中的 C/C++ 代碼執(zhí)行共用的我們 node.js 設(shè)計(jì)的 單進(jìn)程單線(xiàn)程 模型。
Addons 中使用了 Sleep(1000) 那么就真的是會(huì)把我們的線(xiàn)程休眠1秒哦 (機(jī)智的你是不是已經(jīng)發(fā)現(xiàn)了,可以用 C/C++ Addons 在 node.js 中實(shí)現(xiàn)和其他語(yǔ)言一樣的 sleep 呢)。
那么話(huà)說(shuō)回來(lái),這個(gè)問(wèn)題腫莫辦捏?
下一篇進(jìn)階文章 Nodejs C/C++ Addons 非阻塞多線(xiàn)程回調(diào) 我們來(lái)解決下這個(gè)問(wèn)題.??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,283評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 62,953評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,186評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,410評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,642評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,878評(píng)論 1 286
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,654評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評(píng)論 2 373

推薦閱讀更多精彩內(nèi)容