系列文章
- C/C++ Addons 入門(mén) Hello world!
- C/C++ Addons 對(duì)象參數(shù)及回調(diào)函數(shù)
- C/C++ Addons 非阻塞多線(xiàn)程回調(diào)
- 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)題.??