Node8.0 之 Napi 探祕

被叫做可兒的逸軒發表於2017-06-13

本文目錄

  • 簡介
  • Napi簡介
  • 鐵打的hello_world
  • 關於檔案頭
  • 關於基礎資料型別
  • 關於錯誤處理
  • 關於異常處理
    • 相關異常處理函式
  • 關於生命週期
    • 相關函式
    • 關於模組註冊
  • 關於js的型別值
    • 列舉值
    • 物件構造器
    • C->N-api值 轉換函式
    • N-api->C 值轉換函式
    • 獲取全域性例項的函式
    • 關於JavaScript值的抽象操作
  • 關於JavaScript中的屬性
    • 官方示例
    • 索引值的demo
    • 複雜物件的demo
    • 相關結構體
    • 相關函式
  • JavaScript函式相關
  • 關於物件包裹
  • 關於非同步操作

簡介

最近發生了很多事,node終於迎來了8.1.0的更新,同時rust語言也迎來了他的1.18版本,帶來了更多的更新。不過這裡主要想要敘述的還是關於node的新特性napi。由於找了下網上的教程基本都止步於官網的hello_world的demo的成功執行,所以想用自己淺薄的英語知識去啃一下深入的東西。

Napi簡介

由於大部分的二進位制node模組都嚴重依賴於V8引擎的暴露的介面,一旦v8引擎有所變動,就會導致模組的使用出現問題。所以按照程式設計師通用的思路,既然依賴太強,那就抽象一層吧,所以node官方做了這麼一層抽象,使程式設計師不用直接面對V8引擎,而是使用node抽象出來的一層介面,這樣保證了在各個版本之間模組的通用性等等。嗯,這段話是小生自己寫的,不對之處請指出Orz。

鐵打的hello_world

講道理,這段示例其實在各個文章中都大同小異,相信各個看官都看過好幾遍了,不過為了文章的連貫性,小生還是不得不再寫一次。

首先,我們更新node到8.1.0或以上版本

node --version

然後我們全域性安裝node-gyp,小生用的yarn,所以就用yarn安裝了

yarn global add node-gyp

安裝成功後我們新建一個資料夾來做實驗

mkdir napi
cd napi
yarn init -y

yarn初始化專案的方式是init -y,npm是 init -f,不過這個其實沒啥子,主要是初始化package.json而已。然後我們按照以前和以前寫二進位制模組一樣的方式,先建立src目錄,建立.cc檔案,然後建立binding.gyp檔案

mkdir src
touch src/hello.cc
touch binding.gyp

然後我們編輯hello.cc,這裡直接使用官方的demo了:

#include <node_api.h>

// 實際暴露的方法,這裡只是簡單返回一個字串
napi_value HelloMethod (napi_env env, napi_callback_info info) {
    napi_value world;
    napi_create_string_utf8(env, "world", 5, &world);
    return world;
}

// 擴充套件的初始化方法,其中 
// env:環境變數
// exports、module:node模組中對外暴露的物件
void Init (napi_env env, napi_value exports, napi_value module, void* priv) {
    // napi_property_descriptor 為結構體,作用是描述擴充套件暴露的 屬性/方法 的描述
    napi_property_descriptor desc = { "hello", 0, HelloMethod, 0, 0, 0, napi_default, 0 };
    napi_define_properties(env, exports, 1, &desc);  // 定義暴露的方法
}

NAPI_MODULE(hello, Init);  // 註冊擴充套件,副檔名叫做hello,Init為擴充套件的初始化方法複製程式碼

然後我們編輯binding.gyp:

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "./src/hello.cc" ]
    }
  ]
}複製程式碼

接下來直接:

node-gyp rebuild

就可以看到本地多出了build資料夾,並且可以發現編譯後的檔案build/Release/hello.node,所以我們可以直接寫一個js檔案測試我們的demo是否成功了:

//index.js
var a=require('./build/Release/hello')
console.log(a.hello())複製程式碼

執行

node --napi-modules index.js

後發現輸出world,即一切正常,我們完成了一個二進位制包的編寫。由於目前napi還屬於新特性階段,所以執行時需要加上--napi-modules引數。恩,大多數的文章就到此為止了,不過小生還是決定看看官方的api,深入研究下。

官網文件的研究

由於文件很長,沒法一次看完慢慢整理,所以小生一邊看文件,一邊思索一邊測試一邊寫,內容可能會有一點小小的混亂,請見諒。napi文件傳送門

關於檔案頭

由於napi是新特性,所以在執行時請加上引數--napi-modules,同時在cc檔案中需加上#include <node_api.h>頭部

關於基礎資料型別

  • napi_status是一個列舉資料型別,為了節省篇幅,具體定義請點選後與官網檢視,該資料型別表示一個napi函式呼叫是否成功,每當呼叫一個napi的函式後,都會返回該值,表示是否操作的成功與否資訊,例:
napi_value result;
napi_status status = napi_get_element(e object, i, &result);
if (status != napi_ok) {
  //do someting
}複製程式碼

其中napi_ok就是napi_status的列舉取值之一,表示操作成功

  • napi_extended_error_info是一個結構體,在呼叫函式不成功時儲存了較為詳細的錯誤資訊

  • napi_env表示一個上下文的變數

  • napi_value是對所有js的基本值的一個密閉封裝,簡單來說,就是表示一個基本值

  • napi_handle_scope,這是一個抽象型別,用於管理和修改在特定範圍內建立的物件的生命週期,使用napi_open_handle_scope將建立一個上下文環境使用napi_close_handle_scope將關閉這個上下文環境,在關閉這個上下文後,所有在其中宣告的引用都將被關閉。說的簡單點,就是類似於給包了一個大括弧,所有的let屬性宣告週期都只能在這內部。

  • napi_escapable_handle_scope是一個將特定範圍內宣告的值返回到父作用域的一個特殊型別

  • napi_ref是一個抽象型別,用於引用napi_value,讓使用者能管理js值的生命週期

  • napi_callback_info這是傳遞給回撥函式的一個封裝的資料型別,可以用於獲取有關呼叫時的上下文資訊,也可以用於設定回撥函式的返回值

  • 通過Napi暴露給使用者設定的回撥函式指標,設定回撥應該滿足以下函式簽名:

    typedef void (*napi_callback)(napi_env, napi_callback_info);

  • napi_async_execute_callbacknapi_async_complete_callback二者皆是與回撥函式一起執行的函式指標,函式簽名需要滿足以下:

    typedef void (napi_async_execute_callback)(napi_env env, void data);

    typedef void (napi_async_complete_callback)(napi_env env,napi_status status,void data);

關於錯誤處理

所有的napi函式都使用相同的錯誤處理函式,在呼叫之後會返回napi_status,在不出錯正常時會返回napi_ok,如果只是丟擲異常,則會返回napi_pending_exception,在兩者皆不是的情況下需要使用napi_is_exception_pending來判斷是否有異常被掛起。當有錯誤發生時,則需要使用函式napi_get_last_error_info,該函式會將錯誤資訊填充到引數napi_extended_error_info中,函式簽名如下:

NAPI_EXTERN napi_status napi_get_last_error_info(napi_env env,const napi_extended_error_info** result);

呼叫後會返回該次呼叫的napi_status,如果結果是napi_ok,那麼result引數將會被填充成napi_extended_error_info結構體,該結構體如下:

typedef struct napi_extended_error_info {
const char error_message;
void
engine_reserved;
uint32_t engine_error_code;
napi_status error_code;
};

關於異常處理

在使用napi_is_exception_pending確定有異常掛起後,有兩種方式來處理異常。第一種,也是官方推薦的一種,就是簡單處理後直接返回,使程式碼觸發js內的異常,交由js自行處理。第二種,是不推薦的,就是嘗試自行處理異常,可以使用napi_get_and_clear_last_exception來獲取以及清除異常。不過在檢索後,發現無法處理該異常,可以選擇[napi_throw] []來重新丟擲該異常。

相關異常處理函式

  • napi_throw

    NODE_EXTERN napi_status napi_throw(napi_env env, napi_value error);

    該函式返回一個js可接受的錯誤

  • napi_throw_error

    NODE_EXTERN napi_status napi_throw_error(napi_env env, const char* msg);

    該函式丟擲一個純文字的錯誤資訊

  • napi_throw_type_error

    NODE_EXTERN napi_status napi_throw_type_error(napi_env env, const char* msg);

    該函式丟擲一個純文字的型別錯誤資訊

  • napi_throw_range_error

    NODE_EXTERN napi_status napi_throw_range_error(napi_env env, const char* msg);

    該函式丟擲一個純文字的RangeError

  • napi_is_error

    NODE_EXTERN napi_status napi_is_error(napi_env env,napi_value value,bool* result);

    該函式會檢查傳入的napi_value是否是一個錯誤物件

  • napi_create_error

    NODE_EXTERN napi_status napi_create_error(napi_env env, const char* msg);

    該函式會返回一個js純文字錯誤

  • napi_create_type_error

    NODE_EXTERN napi_status napi_create_type_error(napi_env env, const char* msg);

    該函式會返回一個js純文字型別錯誤

  • napi_create_range_error

    NODE_EXTERN napi_status napi_create_range_error(napi_env env, const char* msg);

    該函式會返回一個js純文字RangeError

  • napi_get_and_clear_last_exception

    NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env,napi_value* result);

    這裡官方文件應該是寫錯了,官方文件函式描述是This API returns true if an exception is pending.,很明顯,這個描述是錯的,從前文來看,該函式的效果應該是獲取最近的一次的控制程式碼,並且清除異常

  • napi_is_exception_pending

    NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result);

    這裡官方的描述依舊是This API returns true if an exception is pending.,不過這下就正常了,該函式會在有異常被掛起時返回true

關於生命週期

由於在napi中所有js相關的值都是一個不透明的分裝,預設生命週期是和全域性一致的,有時候處於安全和效能的考慮,需要將一些值得生命週期限制在一定的範圍以內,此時就需要用到上文提到過得open_handle_scope和napi_close_handle_scope來建立和關閉一個上下文環境。示例:

for (int i = 0; i < 1000000; i++) {
  napi_handle_scope scope;
  napi_status status = napi_open_handle_scope(env, &scope);
  if (status != napi_ok) {
    break;
  }
  napi_value result;
  status = napi_get_element(e object, i, &result);
  if (status != napi_ok) {
    break;
  }
  // do something with element
  status = napi_close_handle_scope(env, scope);
  if (status != napi_ok) {
    break;
  }
}複製程式碼

此時,由於限制了作用域,所以每個result的生命週期都被限制在了單次迴圈之內。
在有些時候某些值得生命週期需要大於本身所在區域的週期時,可以將他們放在 napi_open_escapable_handle_scopenapi_close_escapable_handle_scope這兩個函式之間,此間定義的值的生命週期將與父級的作用域的生命週期一致,而不侷限於本身的生命週期。

相關函式簽名:

> NODE_EXTERN napi_status napi_open_handle_scope(napi_env env,napi_handle_scope* result);

> NODE_EXTERN napi_status napi_close_handle_scope(napi_env env,napi_handle_scope scope);

> NODE_EXTERN napi_status napi_open_escapable_handle_scope(napi_env env,napi_handle_scope* result);

> NODE_EXTERN napi_status napi_close_escapable_handle_scope(napi_env env,napi_handle_scope scope);

> NAPI_EXTERN napi_status napi_escape_handle(napi_env env,napi_escapable_handle_scope scope,napi_value escapee,napi_value* result);複製程式碼

關於模組註冊:

Napi的模組註冊使用NAPI_MODULE(addon, Init)方式來註冊,其中Init方法簽名如下:

void Init(napi_env env, napi_value exports, napi_value module, void* priv);

關於js的型別值

Napi建立了一系列的api來負責建立各種型別的JavaScript值。這系列api主要用於以下功能:

  • 建立一個新的JavaScript物件
  • 從原始的c型別轉換到N-api值
  • 從N-api值轉換為原始的c型別
  • 獲取全域性例項,包括undefined和null

列舉值

  • napi_valuetype
typedef enum {
  // ES6 types (corresponds to typeof)
  napi_undefined,
  napi_null,
  napi_boolean,
  napi_number,
  napi_string,
  napi_symbol,
  napi_object,
  napi_function,
  napi_external,
} napi_valuetype;複製程式碼
  • napi_typedarray_type
typedef enum {
  napi_int8_array,
  napi_uint8_array,
  napi_uint8_clamped_array,
  napi_int16_array,
  napi_uint16_array,
  napi_int32_array,
  napi_uint32_array,
  napi_float32_array,
  napi_float64_array,
} napi_typedarray_type;複製程式碼

物件構造器

  • napi_create_array

    napi_status napi_create_array(napi_env env, napi_value* result)

    該函式返回一個JavaScript陣列型別對應的napi值

  • napi_create_array_with_length

    napi_status napi_create_array_with_length(napi_env env,size_t length,napi_value* result)

    該函式返回特定長度的JavaScript陣列型別對應的napi值,但是該函式不能保證在建立陣列時由VM預先分配記憶體,如果需要保證緩衝區必須是可以通過c直接讀取或寫入的連續的記憶體,需要使用napi_create_external_arraybuffer

  • napi_create_arraybuffer

    napi_status napi_create_arraybuffer(napi_env env,size_t byte_length,void* data,napi_value result)

  • napi_create_buffer

    napi_status napi_create_buffer(napi_env env,size_t size,void* data,napi_value result)

  • napi_create_buffer_copy

    napi_status napi_create_buffer_copy(napi_env env,size_t length,const void data,void** result_data,napi_value result)

  • napi_create_external

    napi_status napi_create_external(napi_env env,void data,napi_finalize finalize_cb,void finalize_hint,napi_value* result)

  • napi_create_external_arraybuffer

    napi_status napi_create_external_arraybuffer(napi_env env,void external_data,size_t byte_length,napi_finalize finalize_cb,void finalize_hint,napi_value* result)

  • napi_create_external_buffer

    napi_status napi_create_external_buffer(napi_env env,size_t length,void data,napi_finalize finalize_cb,void finalize_hint,napi_value* result)

  • napi_create_function

    napi_status napi_create_function(napi_env env,const char utf8name,napi_callback cb,void data,napi_value* result)

    該函式返回JavaScript中函式對應的napi值,用於包裝本地函式,使JavaScript可以呼叫它

  • napi_create_object

    napi_status napi_create_object(napi_env env, napi_value* result)

    該函式返回一個預設的JavaScript物件,等同於在JavaScript中呼叫new Object()

  • napi_create_symbol

    api_status napi_create_symbol(napi_env env,const char description,napi_value result)

  • napi_create_typedarray

    napi_status napi_create_typedarray(napi_env env,napi_typedarray_type type,size_t length,napi_value arraybuffer,size_t byte_offset,napi_value* result)

C->N-api值 轉換函式

  • napi_create_number

    napi_status napi_create_number(napi_env env, double value, napi_value* result)

  • napi_create_string_utf16

    napi_status napi_create_string_utf16(napi_env env,const char16_t str,size_t length,napi_value result)

  • napi_create_string_utf8

    napi_status napi_create_string_utf8(napi_env env,const char str,size_t length,napi_value result)

N-api->C 值轉換函式

  • napi_get_array_length

    napi_status napi_get_array_length(napi_env env,napi_value value,uint32_t* result)

  • napi_get_arraybuffer_info

    napi_status napi_get_arraybuffer_info(napi_env env,napi_value arraybuffer,void* data,size_t byte_length)

  • napi_get_buffer_info

    napi_status napi_get_buffer_info(napi_env env,napi_value value,void* data,size_t length)

  • napi_get_prototype

    napi_status napi_get_prototype(napi_env env,napi_value object,napi_value* result)

  • napi_get_typedarray_info

    napi_status napi_get_typedarray_info(napi_env env,napi_value typedarray,napi_typedarray_type type,size_t length,void* data,napi_value arraybuffer,size_t* byte_offset)

  • napi_get_value_bool

    napi_status napi_get_value_bool(napi_env env, napi_value value, bool* result)

  • napi_get_value_double

    napi_status napi_get_value_double(napi_env env,napi_value value,double* result)

  • napi_get_value_external

    napi_status napi_get_value_external(napi_env env,napi_value value,void** result)

  • napi_get_value_int32

    napi_status napi_get_value_int32(napi_env env,napi_value value,int32_t* result)

  • napi_get_value_int64

    napi_status napi_get_value_int64(napi_env env,napi_value value,int64_t* result)

  • napi_get_value_string_length

    napi_status napi_get_value_string_length(napi_env env,napi_value value,int* result)

  • napi_get_value_string_utf8

    napi_status napi_get_value_string_utf8(napi_env env,napi_value value,char buf,size_t bufsize,size_t result)

  • napi_get_value_string_utf16

    napi_status napi_get_value_string_utf16(napi_env env,napi_value value,char16_t buf,size_t bufsize,size_t result)

  • napi_get_value_uint32

    napi_status napi_get_value_uint32(napi_env env,napi_value value,uint32_t* result)

獲取全域性例項的函式

  • napi_get_boolean

    napi_status napi_get_boolean(napi_env env, bool value, napi_value* result)

  • napi_get_global

    napi_status napi_get_global(napi_env env, napi_value* result)

  • napi_get_null

    napi_status napi_get_null(napi_env env, napi_value* result)

  • napi_get_undefined

    napi_status napi_get_undefined(napi_env env, napi_value* result)

關於JavaScript值的抽象操作

  • napi_coerce_to_bool

    napi_status napi_coerce_to_bool(napi_env env,napi_value value,napi_value* result)

    對於JavaScript中的ToBoolean操作的一個實現

  • napi_coerce_to_number

    napi_status napi_coerce_to_number(napi_env env,napi_value value,napi_value* result)

    對於JavaScript中的ToNumber操作的一個實現

  • napi_coerce_to_object

    napi_status napi_coerce_to_object(napi_env env,napi_value value,napi_value* result)

    對於JavaScript中的ToObject操作的一個實現

  • napi_coerce_to_string

    napi_status napi_coerce_to_string(napi_env env,napi_value value,napi_value* result)

    對於JavaScript中的ToString操作的一個實現

  • napi_typeof

    napi_status napi_typeof(napi_env env, napi_value value, napi_valuetype* result)

    對於JavaScript中的typeof操作的一個實現

  • napi_instanceof

    napi_status napi_instanceof(napi_env env,napi_value object,napi_value constructor,bool* result)

    對於JavaScript中的instanceof操作的一個實現

  • napi_is_array

    napi_status napi_is_array(napi_env env, napi_value value, bool* result)

    對於JavaScript中的isArrat函式的一個實現

  • napi_is_arraybuffer

    napi_status napi_is_arraybuffer(napi_env env, napi_value value, bool* result)

  • napi_is_buffer

    napi_status napi_is_buffer(napi_env env, napi_value value, bool* result)

  • napi_is_error

    napi_status napi_is_error(napi_env env, napi_value value, bool* result)

  • napi_is_typedarray

    napi_status napi_is_typedarray(napi_env env, napi_value value, bool* result)

  • napi_strict_equals

    napi_status napi_strict_equals(napi_env env,napi_value lhs,napi_value rhs,bool* result)

    比較傳入的左值和右值是否嚴格相等

關於JavaScript中的屬性

在JavaScript中,屬性一般為鍵和值的元組,napi中所有屬性鍵都可以用一下形式表示:

  • 命名:一個簡單的utf8字串
  • 整數索引:一個unit32_t表示的索引值
  • JavaScript值:在napi中有napi_value表示

官方示例:

比如如下JavaScript程式碼:

const obj = {};
obj.myProp = 123;複製程式碼

轉化成napi模式變成了這樣:

napi_status status = napi_generic_failure;

// const obj = {}
napi_value obj, value;
status = napi_create_object(env, &obj);
if (status != napi_ok) return status;

// Create a napi_value for 123
status = napi_create_number(env, 123, &value);
if (status != napi_ok) return status;

// obj.myProp = 123
status = napi_set_named_property(env, obj, "myProp", value);
if (status != napi_ok) return status;複製程式碼

索引值的demo

const arr = [];
arr[123] = 'hello';複製程式碼

napi_status status = napi_generic_failure;

// const arr = []
napi_value arr, value;
status = napi_create_array(env, &arr);
if (status != napi_ok) return status;

// const value = arr[123]
status = napi_get_element(env, arr, 123, &value);
if (status != napi_ok) return status;複製程式碼

複雜物件的demo

const obj = {};
Object.defineProperties(obj, {
  'foo': { value: 123, writable: true, configurable: true, enumerable: true },
  'bar': { value: 456, writable: true, configurable: true, enumerable: true }
});複製程式碼

napi的格式為:

napi_status status = napi_status_generic_failure;

// const obj = {};
napi_value obj;
status = napi_create_obj(env, &obj);
if (status != napi_ok) return status;

// Create napi_values for 123 and 456
napi_value fooValue, barValue;
status = napi_create_number(env, 123, &fooValue);
if (status != napi_ok) return status;
status = napi_create_number(env, 456, &barValue);
if (status != napi_ok) return status;

// Set the properties
napi_property_descriptors descriptors[] = {
  { "foo", fooValue, 0, 0, 0, napi_default, 0 },
  { "bar", barValue, 0, 0, 0, napi_default, 0 }
}
status = napi_define_properties(env,
                                obj,
                                sizeof(descriptors) / sizeof(descriptors[0]),
                                descriptors);
if (status != napi_ok) return status;複製程式碼

相關結構體

  • napi_property_attributes
    typedef enum {
    napi_default = 0,
    napi_read_only = 1 << 0,
    napi_dont_enum = 1 << 1,
    napi_dont_delete = 1 << 2,
    napi_static_property = 1 << 10,
    } napi_property_attributes;複製程式碼

該結構體是用於控制JavaScript物件上設定的屬性的行為的標誌的,每個屬性的示例參見官方傳送門

  • napi_property_descriptor
typedef struct {
  const char* utf8name;

  napi_callback method;
  napi_callback getter;
  napi_callback setter;
  napi_value value;

  napi_property_attributes attributes;
  void* data;
} napi_property_descriptor;複製程式碼

該結構體用來描述複雜物件具體的屬性,每個引數的含義參見官方 傳送門

相關函式

  • napi_get_property_names

    napi_status napi_get_property_names(napi_env env,napi_value object,napi_value* result);

    相當於es6中的keys函式

  • napi_set_property

    napi_status napi_set_property(napi_env env,napi_value object,napi_value key,napi_value value);

    為物件的某個屬性賦值

  • napi_get_property

    napi_status napi_get_property(napi_env env,napi_value object,napi_value key,napi_value* result);

    獲取物件某個屬性的值

  • napi_has_property

    napi_status napi_has_property(napi_env env,napi_value object,napi_value key,bool* result);

    檢測該物件是否存在對應的鍵

  • napi_set_named_property

    napi_status napi_set_named_property(napi_env env,napi_value object,const char* utf8Name,napi_value value);

    等價於直接呼叫napi_set_property,不過限定了字串為鍵名的情況

  • napi_get_named_property

    napi_status napi_get_named_property(napi_env env,napi_value object,const char utf8Name,napi_value result);

    等價於呼叫napi_get_property,限定於字串為鍵名的情況

  • napi_has_named_property

    napi_status napi_has_named_property(napi_env env,napi_value object,const char utf8Name,bool result);

    等價於呼叫napi_has_property,限定於字串鍵名情況

  • napi_set_element

    napi_status napi_set_element(napi_env env,napi_value object,uint32_t index,napi_value value);

    為一個物件設定索引鍵屬性

  • napi_get_element

    napi_status napi_get_element(napi_env env,napi_value object,uint32_t index,napi_value* result);

    獲取一個物件的索引屬性

  • napi_has_element

    napi_status napi_has_element(napi_env env,napi_value object,uint32_t index,bool* result);

      查詢函式是否包含某個索引複製程式碼
  • napi_define_properties

    napi_status napi_define_properties(napi_env env,napi_value object,size_t property_count,const napi_property_descriptor* properties);

這個函式用於高效的建立複雜物件,傳入napi_property_descriptor陣列,用於建立複雜物件

JavaScript函式相關

Napi提供了一些列的api,來允許JavaScript呼叫本機程式碼,使用napi_callback型別表示回撥函式。這類api允許回撥函式進行一下操作:

  • 獲取有關呼叫回撥的上下文資訊
  • 獲取傳入回撥的引數
  • 從回撥中返回一個napi_value

另外,napi還提供了一組允許原生程式碼呼叫JavaScript函式的函式,可以呼叫像常規JavaScript函式或作為建構函式的函式

  • napi_call_function

    napi_status napi_call_function(napi_env env,napi_value recv,napi_value func,int argc,const napi_value argv,napi_value result)

    該函式用於呼叫js函式,比如以下示例,便是從全域性物件中獲取了全域性js函式,並呼叫。

假設全域性存在以下函式:

function AddTwo(num) {
  return num + 2;
}複製程式碼

然後在下面的程式碼中呼叫:

// Get the function named "AddTwo" on the global object
napi_value global, add_two, arg;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) return;

status = napi_get_named_property(env, global, "AddTwo", &add_two);
if (status != napi_ok) return;

// const arg = 1337
status = napi_create_number(env, 1337, &arg);
if (status != napi_ok) return;

napi_value* argv = &arg;
size_t argc = 1;

// AddTwo(arg);
napi_value return_val;
status = napi_call_function(env, global, add_two, argc, argv, &return_val);
if (status != napi_ok) return;

// Convert the result back to a native type
int32_t result;
status = napi_get_value_int32(env, return_val, &result);
if (status != napi_ok) return;複製程式碼
  • napi_create_function

    napi_status napi_create_function(napi_env env,const char utf8name,napi_callback cb,void data,napi_value* result);

    該函式用於建立一個js中可以呼叫的函式,示例如下:

void SayHello(napi_env env, napi_callback_info info) {
  printf("Hello\n");
}

void Init(napi_env env, napi_value exports, napi_value module, void* priv) {
  napi_status status;

  napi_value fn;
  status =  napi_create_function(env, NULL, SayHello, NULL, &fn);
  if (status != napi_ok) return;

  status = napi_set_named_property(env, exports, "sayHello", fn);
  if (status != napi_ok) return;
}

NAPI_MODULE(addon, Init)複製程式碼
  • napi_get_cb_info

    napi_status napi_get_cb_info(napi_env env,napi_callback_info cbinfo,size_t argc,napi_value argv,napi_value thisArg,void* data)

    該函式用於在回撥函式中從給定的回撥資訊中檢索有關呼叫的詳細資訊

  • napi_is_construct_call

    napi_status napi_is_construct_call(napi_env env,napi_callback_info cbinfo,bool* result)

    該函式用於檢測當前回撥是否由構造器呼叫

  • napi_new_instance

    napi_status napi_new_instance(napi_env env,napi_value cons,size_t argc,napi_value argv,napi_value result)

    該函式用於使用給定的napi_value來例項化一個新的JavaScript值,該值代表該物件的建構函式。示例如下:

function MyObject(param) {
  this.param = param;
}

const arg = 'hello';
const value = new MyObject(arg);複製程式碼

在napi中使用該建構函式大致就是這樣:

// Get the constructor function MyObject
napi_value global, constructor, arg, value;
napi_status status = napi_get_global(env, &global);
if (status != napi_ok) return;

status = napi_get_named_property(env, global, "MyObject", &constructor);
if (status != napi_ok) return;

// const arg = "hello"
status = napi_create_string_utf8(env, "hello", -1, &arg);
if (status != napi_ok) return;

napi_value* argv = &arg;
size_t argc = 1;

// const value = new MyObject(arg)
status = napi_new_instance(env, constructor, argc, argv, &value);複製程式碼
  • napi_make_callback

    napi_status napi_make_callback(napi_env env,napi_value recv,napi_value func,int argc,const napi_value argv,napi_value result)

    該方法允許從本機外掛呼叫JavaScript函式物件。 這個API類似於napi_call_function。 但是,當從非同步操作返回時(當堆疊中沒有其他指令碼)時,它用於從本機程式碼呼叫回JavaScript

物件包裹

napi提供了一種"包裝"C++類和例項的方法,以便於在JavaScript中呼叫類建構函式和方法.
napi_define_class api定義了一個具有建構函式、靜態屬性和方法的JavaScript類,以及與c++類對應的例項屬性和方法。當JavaScript呼叫建構函式時,會使用napi_wrap將一個新的c++例項包裝在一個JavaScript對像中,然後返回包裝物件,當訪問對應的方法或屬性訪問器時,呼叫相應的napi_callback C++函式。
ps:由於物件包裹的內容很麻煩,所以並未在本文中探討

  • napi_define_class

    napi_status napi_define_class(napi_env env,

                         const char* utf8name,
                         napi_callback constructor,
                         void* data,
                         size_t property_count,
                         const napi_property_descriptor* properties,
                         napi_value* result);複製程式碼
  • napi_wrap

    napi_status napi_wrap(napi_env env,

                 napi_value js_object,
                 void* native_object,
                 napi_finalize finalize_cb,
                 void* finalize_hint,
                 napi_ref* result);複製程式碼
  • napi_unwrap

    napi_status napi_unwrap(napi_env env,napi_value js_object,void** result);

關於非同步操作

在napi中實現了一系列函式用於管理非同步操作,使用napi_create_async_work和napi_delete_async_work建立/刪除例項。
其中執行回撥會完成回撥分別是在程式準備執行時以及執行完成時所呼叫的函式:

typedef void (napi_async_execute_callback)(napi_env env,void data);

typedef void (napi_async_complete_callback)(napi_env env,napi_status status,void data);

其中 引數data是呼叫napi_create_async_work時所傳遞的資料。
建立後可以使用napi_queue_async_work函式讓非同步工作佇列排隊執行

NAPI_EXTERN napi_status napi_queue_async_work(napi_env env,napi_async_work work);

  • napi_create_async_work

    NAPI_EXTERN
    napi_status napi_create_async_work(napi_env env,napi_async_execute_callback execute,napi_async_complete_callback complete,void data,napi_async_work result);

  • napi_delete_async_work

    NAPI_EXTERN napi_status napi_delete_async_work(napi_env env,napi_async_work work);

  • napi_cancel_async_work

    NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env,napi_async_work work);

相關文章