11

Rust和node.js:天生一对

 3 years ago
source link: https://www.myfreax.com/rust-and-node-js-a-match-made-in-heaven/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

node.js是一个非常流行的JavaScript运行时,用于编写后端应用程序。 它的灵活性和非阻塞性使其成为编写API首要选择。

由于它是脚本语言,JavaScript可能比较慢。 但由于V8的优化,它足以实现实际应用。 也就是说,Node.js对计算密集任务不利; 由于它是单线程的,因此阻塞主线程进行长期计算是不值得的。 这就是worker线程使用的地方。Node.js对worker线程有支持,因此它可用于执行密集型计算。

只要worker线程达到一定程度,JavaScript仍然很慢。 此外,在所有支持的LTS版本中没有worker线程。 幸运的是,我们可以使用Rust构建Node.js的原声模块。 FFI是另一种替代方案,但它比原生方法慢。 Rust更快,并具有无限制的并发。 由于Rust有一个非常小的运行时(其实这不应该称为运行时),生成的二进制大小也非常小。

什么是Rust?

RURT是Mozilla的系统编程语言。 它默认可以调用C库,并将函数导出到C。

可以通过各种方法在Node.js上下文中调用Rust。 下面列出了最广泛使用的一些示例。

  • 您可以使用FFI,在Node.js和Rust中,但这非常慢
  • 您可以使用webassembly来创建node_module,但所有Node.js函数都不可用
  • 您可以使用原生模块

什么是native addon?

Node.js addons是用动态链接的C ++编写的共享对象。 您可以使用require()函数将它们加载到Node.js中,并使用它们,就像它们是普通node.js模块一样。 它们主要在Node.js和C/C ++库中的JavaScript运行时之间提供接口。

原生addon提供了一个简单的接口,通过在V8运行时加载它来与另一个二进制一起使用。 在跨语言调用是非常快速和安全的。 目前,Node.js支持两种类型的addons方法:C ++ Addons和N-API C ++/C插件。

C++ addons

C ++ addons是可以由Node.js安装并在运行时使用的对象。 由于C ++是编译语言,因此这些插件非常快。 C ++具有各种生产库,可用于Node.js生态系统。 许多流行的库使用原生插件来提高性能和代码质量。

N-API C++/C addons

C ++ Addons的主要问题是每个更改时您需要将它们重新编译到底层JavaScript运行时。 它导致维护迟缓的问题。 N-API尝试通过引入标准应用二进制接口(ABI)来消除此问题。 C头文件仍然向后兼容。 这意味着您可以使用特定版本的node.js编译的Addons,任何版本大于它都会被编译。 您将使用此方法实现您的插件。

从哪里开始Rust?

Rust可以模拟C库的行为。 换句话说,它以导出C可以理解的使用格式。 rust调用C函数来访问和使用Node.js提供的API。 这些API提供了用于创建JavaScript string,array,number,error,object,function等。 但我们需要讲述一些rust的外部功能,结构,指针等。

#[repr(C)]
struct MyRustStruct {
    a: i32,
}
extern "C" fn rust_world_callback(target: *mut RustObject, a: i32) {
    println!("Function is called from C world", a);
    unsafe {
        // Do something on rust struct
        (*target).a = a;
    }
}
extern {
   fn register_callback(target: *mut MyRustStruct,
                        cb: extern fn(*mut MyRustStruct, i32)) -> i32;
   fn trigger_callback();
}

Rust以不同方式在内存中存放结构,因此我们需要告诉它使用C风格。 通过手动创建这些函数是一种痛苦,因此我们将使用称为nodejs-sys的箱子,它为使用N-API创建一个很好的bindgen定义。

bindgen自动生成RUST FFI绑定到C和C ++库。

GIF of the Joker Saying

设置您的项目

对于本教程,您必须在系统上具有node.jsRust ,包括CARGONPM 。 建议使用Rustup来安装Rust和nvm安装node.js.

创建名为rust-addon的目录,并通过运行npm init初始化新的NPM项目。 接下来,使用cargo init --lib 初始化一个cargo项目。 您的项目目录应该如下所示:

├── Cargo.toml
├── package.json
└── src
    └── lib.rs

配置Rust并编译为addon

我们需要Rust编译为动态C库或对象。 配置Cargo以编译到Linux上的.so文件,在OSX上的.dylib,在Windows上的.dll。 rust可以使用Rustc标识生成许多不同类型的动态库。

[package]
name = "rust-addon"
version = "0.1.0"
authors = ["Anshul Goyal <[email protected]>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
crate-type=["cdylib"]

[dependencies]
nodejs-sys = "0.2.0"

lib键提供配置RUSTC的选项。 name键以lib{name}的形式给出了库名称,而type提供了库的类型,它应该被编译为 cdylibrlibcdylib创建一个动态链接的C库。 此共享对象的行为类似于C库。

n-api入门

创建我们的N-API库。 我们需要添加依赖项。 nodejs-sys提供napi-header文件所需的绑定。 napi_register_module_v1是插件的入口点。 N-API文档建议模块注册的N-API_MODULE_INIT宏,它编译为napi_register_module_v1函数。

Node.js调用此函数并使用名为napi_env的不透明指针提供它,这是指在JavaScript运行时中的模块的配置。 后者是代表JavaScript值的另一个不透明指针,其实际上是称为导出的对象。 这些导出与JavaScript中的Node.js模块提供的那些导出相同。

use nodejs_sys::{napi_create_string_utf8, napi_env, napi_set_named_property, napi_value};
use std::ffi::CString;
#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
    env: napi_env,
    exports: napi_value,
) -> nodejs_sys::napi_value {
// creating a C string
    let key = CString::new("hello").expect("CString::new failed");
// creating a memory location where the pointer to napi_value will be saved
    let mut local: napi_value = std::mem::zeroed();
// creating a C string
    let value = CString::new("world!").expect("CString::new failed");
// creating napi_value for the string
    napi_create_string_utf8(env, value.as_ptr(), 6, &mut local);
// setting the string on the exports object
    napi_set_named_property(env, exports, key.as_ptr(), local);
// returning the object 
    exports
}

Rust有自己String类型和借用的C字符串的表示形式。 两者都始终在UTF-8编码中,并且可以在中间包含空字节。 如果您查看构成字符串的字节,其中可能存在\0。 两个Stringstr明确地存储它们的长度; 字符串结束时像C字符串没有空终止符号。

rust字符串与C中的生锈字符串非常不同,因此我们需要在使用N-API函数之前将我们的rust字符串更改为C字符串。 由于exports是由exports表示的对象,我们可以将函数,字符串,array或任何其他JavaScript对象添加为键值对。

要将键添加到JavaScript对象,您可以使用N-API提供的方法napi_set_named_property。 此函数帮助我们添加属性到对象; 指向一个字符串的指针将被用作我们属性的密钥;指针指向javascript值,可以是字符串,array等; napi_env,它在rust和node.js之间起到锚作用。

您可以使用N-API函数来创建任何JavaScript值。 例如,我们在此处使用napi_create_string_utf8来创建一个字符串。 我们在环境中传递指向字符串的指针,字符串的长度,以及一个指向空存储位置的指针,可以将指针写入新创建的值。 所有这些代码都不安全,因为它包含许多对外部函数的调用,编译器无法提供保证。

重要的是要理解nodejs-sys 它为您使用的函数提供所需的定义,而不是其实现。 N-API实现包含在Node.js中,您可以从rust代码中调用它。

在Node.js中使用原生模块addon

下一步是为不同的操作系统添加链接配置,然后编译它。

创建一个build.rs文件并添加一些配置标识,用于将N-API文件链接在不同的操作系统上。

fn main() {
    println!("cargo:rustc-cdylib-link-arg=-undefined");
    if cfg!(target_os = "macos") {
        println!("cargo:rustc-cdylib-link-arg=dynamic_lookup");
    }
}

您的目录应该如下所示:

├── build.rs
├── Cargo.lock
├── Cargo.toml
├── index.node
├── package.json
├── src
    └── lib.rs

现在您需要编译rust addon。 您可以使用简单的命令cargo build --release。 这将花一些时间在第一次运行的时候。

编译模块后,将此二进制文件的副本从./target/release/libnative.so创建到根目录,并将其重命名为index.node。 由cargo创建的二进制文件可能具有不同的扩展或名称,具体取决于您的crate设置和操作系统。

现在您可以在Node.js中require并使用它。 您也可以在脚本中使用它。 例如:

let addon=require('./index.node');
console.log(addon.hello);
Using the Addon in Node

接下来,我们将继续创建函数,数组和promises,并使用libuv thread-pool执行繁重的任务而不阻止主线程。

深入理解N-API

现在您知道如何使用N-API和RUST实现常见模式。 一个非常常见的模式是导出函数,可以作为库或node.js模块的供用户调用。 让我们开始创建函数。

您应该使用napi_create_function创建函数,以便您可以从Node.js使用它们。 您可以将这些函数添加为Node.js属性导出。

JavaScript函数也由napi_value指针表示。 n-api函数非常易于创建和使用。

use nodejs_sys::{
    napi_callback_info, napi_create_function, napi_create_string_utf8, napi_env,
    napi_set_named_property, napi_value,
};
use std::ffi::CString;
pub unsafe extern "C" fn say_hello(env: napi_env, _info: napi_callback_info) -> napi_value {
// creating  a javastring string
    let mut local: napi_value = std::mem::zeroed();
    let p = CString::new("Hello from rust").expect("CString::new    failed");
    napi_create_string_utf8(env, p.as_ptr(), 13, &mut local);
// returning the javascript string
    local
}
#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
    env: napi_env,
    exports: napi_value,
) -> nodejs_sys::napi_value {
// creating a C String
    let p = CString::new("myFunc").expect("CString::new failed");
// creating a location where pointer to napi_value be written
    let mut local: napi_value = std::mem::zeroed();
    napi_create_function(
        env,
// pointer to function name
        p.as_ptr(),
// length of function name
        5,
// rust function
        Some(say_hello),
// context which can be accessed by the rust function
        std::ptr::null_mut(),
// output napi_value
        &mut local,
    );
// set function as property 
    napi_set_named_property(env, exports, p.as_ptr(), local);
// returning exports
    exports
}
Function Created With N-API

在上面的示例中, 我们使用napi_create_function创建了一个函数,函数命名为say_hello,它有以下参数:

  • napi_env
  • 函数名称的字符串,用于JavaScript函数
  • 函数名称长度
  • 当JavaScript调用新创建的函数时执行的函数
  • 用户可以传递上下文数据并从RUST访问上下文
  • 可以保存指向JavaScript函数的指针
  • 当您创建此函数时,将其添加为exports对象的属性,以便您可以将其从JavaScript中使用

Rust的函数必须具有相同的签名,如示例所示。 我们将讨论下一步如何访问函数内的参数使用napi_callback_info。 我们也可以从函数和其他参数访问它。

函数参数非常重要。 N-API提供访问这些参数的方法。 napi_callback_info提供指向javascript侧的函数的详细信息的指针。

use nodejs_sys::{
    napi_callback_info, napi_create_double, napi_create_function, napi_env, napi_get_cb_info,
    napi_get_value_double, napi_set_named_property, napi_value,
};
use std::ffi::CString;

pub unsafe extern "C" fn add(env: napi_env, info: napi_callback_info) -> napi_value {
// creating a buffer where napi_value of argument be written
    let mut buffer: [napi_value; 2] = std::mem::MaybeUninit::zeroed().assume_init();
// max number of arguments
    let mut argc = 2 as usize;
// getting arguments and value of this
    napi_get_cb_info(
        env,
        info,
        &mut argc,
        buffer.as_mut_ptr(),
        std::ptr::null_mut(),
        std::ptr::null_mut(),
    );
// converting napi to f64
    let mut x = 0 as f64;
    let mut y = 0 as f64;
    napi_get_value_double(env, buffer[0], &mut x);
    napi_get_value_double(env, buffer[1], &mut y);
// creating the return value
    let mut local: napi_value = std::mem::zeroed();
    napi_create_double(env, x + y, &mut local);
// returning the result
    local
}

#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
    env: napi_env,
    exports: napi_value,
) -> nodejs_sys::napi_value {
// creating a function name
    let p = CString::new("myFunc").expect("CString::new failed");
    let mut local: napi_value = std::mem::zeroed();
// creating the function
    napi_create_function(
        env,
        p.as_ptr(),
        5,
        Some(add),
        std::ptr::null_mut(),
        &mut local,
    );
// setting function as property
    napi_set_named_property(env, exports, p.as_ptr(), local);
// returning exports
    exports
}
Accessing Arguments With N-API

使用napi_get_cb_info获取参数。 必须提的参数:

  • napi_env
  • 预期参数的数量
  • 一个缓冲区,其中参数可以写为napi_value
  • 创建JavaScript函数时将元数据存储在提供的内存中
  • 可以写入该值指针的存储位置

我们需要创建一个具有内存位置的数组,其中C可以向参数写一个指针,我们可以将此指针缓冲区传递给N-API函数。 我们也得到了this,但在这个例子中我们没有使用它。

使用字符串作为参数

大多数情况下,您需要使用JavaScript中的字符串。 创建和获取字符串的值非常简单。 使用napi_get_value_string_utf8并调用此函数两次:第一次获取长度和第二次以获取字符串的值。

use nodejs_sys::{
    napi_callback_info, napi_create_function, napi_env, napi_get_cb_info, napi_get_undefined,
    napi_get_value_string_utf8, napi_set_named_property, napi_value,
};

use std::ffi::CString;

pub unsafe extern "C" fn print(env: napi_env, info: napi_callback_info) -> napi_value {
// creating a buffer of arguments
    let mut buffer: [napi_value; 1] = std::mem::MaybeUninit::zeroed().assume_init();
    let mut argc = 1 as usize;
// getting arguments
    napi_get_cb_info(
        env,
        info,
        &mut argc,
        buffer.as_mut_ptr(),
        std::ptr::null_mut(),
        std::ptr::null_mut(),
    );
    let mut len = 0;
// getting length by passing null buffer
    napi_get_value_string_utf8(env, buffer[0], std::ptr::null_mut(), 0, &mut len);
    let size = len as usize;
// creating a buffer where string can be placed
    let mut ve: Vec<u8> = Vec::with_capacity(size + 1);
    let raw = ve.as_mut_ptr();
// telling rust not manage the vector
    std::mem::forget(ve);
    let mut cap = 0;
// getting the string value from napi_value
    let _s = napi_get_value_string_utf8(env, buffer[0], raw as *mut i8, size + 1, &mut cap);
    let s = String::from_raw_parts(raw, cap as usize, size);
// printing the string
    println!("{}", s);
// creating an undefined
    let mut und: napi_value = std::mem::zeroed();
    napi_get_undefined(env, &mut und);
// returning undefined
    und
}

#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
    env: napi_env,
    exports: napi_value,
) -> nodejs_sys::napi_value {
    let p = CString::new("myFunc").expect("CString::new failed");
    let mut local: napi_value = std::mem::zeroed();
    napi_create_function(
        env,
        p.as_ptr(),
        5,
        Some(print),
        std::ptr::null_mut(),
        &mut local,
    );
    napi_set_named_property(env, exports, p.as_ptr(), local);
    exports
}
String Arguments in N-API

您需要将几个参数传递给napi_create_string_utf8以创建字符串。 如果将空指针传递为缓冲区,则需要指定字符串的长度。 需要以下参数:

  • napi_env
  • napi_value指向javascript side中的字符串
  • 如果null给出字符串的长度,则要写字符串的缓冲区
  • buffer的长度
  • 写入缓冲区的字节

使用promises和Libuv线程池

阻塞Node.js的主线程以进行计算,这不是一个好主意。 您可以使用Libuv线程进行繁重任务。

首先,创造一个promise。 promise将根据您工作的成功与否来决定reject或resolve。 为此,您需要创建三个函数。 第一个从JavaScript调用,并且控件将传递给第二个函数,它在Libuv线程上运行,无法访问JavaScript。 第三个函数在第二个之后调用具有访问JavaScript侧能力。 您可以使用libuv线程的napi_create_async_work方法。

创建一个promise

创建一个承诺,只需使用napi_create_promise。 这将提供一个指针napi_deferred,然后使用以下函数resolve或reject承诺:

  • napi_resolve_deferred
  • napi_reject_deferred
  • napi_resolve_deferred
  • napi_reject_deferred

您可以创建并抛出rust代码的错误使用napi_create_errornapi_throw_error。 每个N-API函数都返回一个napi_status,应该检查这些值。

以下示例显示如何安排异步工作。

use nodejs_sys::{
    napi_async_work, napi_callback_info, napi_create_async_work, napi_create_error,
    napi_create_function, napi_create_int64, napi_create_promise, napi_create_string_utf8,
    napi_deferred, napi_delete_async_work, napi_env, napi_get_cb_info, napi_get_value_int64,
    napi_queue_async_work, napi_reject_deferred, napi_resolve_deferred, napi_set_named_property,
    napi_status, napi_value,
};
use std::ffi::c_void;
use std::ffi::CString;

#[derive(Debug, Clone)]
struct Data {
    deferred: napi_deferred,
    work: napi_async_work,
    val: u64,
    result: Option<Result<u64, String>>,
}

pub unsafe extern "C" fn feb(env: napi_env, info: napi_callback_info) -> napi_value {
    let mut buffer: Vec<napi_value> = Vec::with_capacity(1);
    let p = buffer.as_mut_ptr();
    let mut argc = 1 as usize;
    std::mem::forget(buffer);
    napi_get_cb_info(
        env,
        info,
        &mut argc,
        p,
        std::ptr::null_mut(),
        std::ptr::null_mut(),
    );
    let mut start = 0;
    napi_get_value_int64(env, *p, &mut start);
    let mut promise: napi_value = std::mem::zeroed();
    let mut deferred: napi_deferred = std::mem::zeroed();
    let mut work_name: napi_value = std::mem::zeroed();
    let mut work: napi_async_work = std::mem::zeroed();
    let async_name = CString::new("async fibonaci").expect("Error creating string");
    napi_create_string_utf8(env, async_name.as_ptr(), 13, &mut work_name);
    napi_create_promise(env, &mut deferred, &mut promise);
    let v = Data {
        deferred,
        work,
        val: start as u64,
        result: None,
    };
    let data = Box::new(v);
    let raw = Box::into_raw(data);
    napi_create_async_work(
        env,
        std::ptr::null_mut(),
        work_name,
        Some(perform),
        Some(complete),
        std::mem::transmute(raw),
        &mut work,
    );
    napi_queue_async_work(env, work);
    (*raw).work = work;
    promise
}

pub unsafe extern "C" fn perform(_env: napi_env, data: *mut c_void) {
    let mut t: Box<Data> = Box::from_raw(std::mem::transmute(data));
    let mut last = 1;
    let mut second_last = 0;
    for _ in 2..t.val {
        let temp = last;
        last = last + second_last;
        second_last = temp;
    }
    t.result = Some(Ok(last));
    Box::into_raw(task);
}

pub unsafe extern "C" fn complete(env: napi_env, _status: napi_status, data: *mut c_void) {
    let t: Box<Data> = Box::from_raw(std::mem::transmute(data));
    let v = match t.result {
        Some(d) => match d {
            Ok(result) => result,
            Err(_) => {
                let mut js_error: napi_value = std::mem::zeroed();
                napi_create_error(
                    env,
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    &mut js_error,
                );
                napi_reject_deferred(env, t.deferred, js_error);
                napi_delete_async_work(env, t.work);
                return;
            }
        },
        None => {
            let mut js_error: napi_value = std::mem::zeroed();
            napi_create_error(
                env,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                &mut js_error,
            );
            napi_reject_deferred(env, t.deferred, js_error);
            napi_delete_async_work(env, t.work);
            return;
        }
    };
    let mut obj: napi_value = std::mem::zeroed();
    napi_create_int64(env, v as i64, &mut obj);
    napi_resolve_deferred(env, t.deferred, obj);

    napi_delete_async_work(env, t.work);
}

#[no_mangle]
pub unsafe extern "C" fn napi_register_module_v1(
    env: napi_env,
    exports: napi_value,
) -> nodejs_sys::napi_value {
    let p = CString::new("myFunc").expect("CString::new failed");
    let mut local: napi_value = std::mem::zeroed();
    napi_create_function(
        env,
        p.as_ptr(),
        5,
        Some(feb),
        std::ptr::null_mut(),
        &mut local,
    );
    napi_set_named_property(env, exports, p.as_ptr(), local);
    exports
}

我们创建了一个结构,用于将指针存储到我们napi_async_worknapi_deferred以及我们的输出。 最初,输出是None。 然后我们创建了一个promise,它提供我们的数据中保存的deferred。 我们的所有函数都可以使用此数据。

接下来,我们将数据转换为原始数据并将其传递给napi_create_async_work 函数与其他回调。 我们返回了我们创建的promise,执行,并将数据转换回struct。

在Libuv线程上完成一次perform,从主线程调用complete,以及先前操作和数据的状态。 现在我们可以reject或resolve我们的任务并从队列中删除。

回顾写过的代码

创建一个名为feb的函数,将导出到JavaScript。 此函数将返回promise并调度一个任务在libuv线程池中。

您可以通过使用napi_create_async_work创建promise来实现这一目标,并将两个函数传递给它。 一个是在libuv线程上执行的,另一个在主线程上。

由于您只能从主线程执行javascript,因此您必须仅从主线程resolve或reject promise。 该代码包括大量不安全的函数。

feb 函数

pub unsafe extern "C" fn feb(env: napi_env, info: napi_callback_info) -> napi_value {
    let mut buffer: Vec<napi_value> = Vec::with_capacity(1);
    let p = buffer.as_mut_ptr();
    let mut argc = 1 as usize;
    std::mem::forget(buffer);
// getting arguments for the function
    napi_get_cb_info(
        env,
        info,
        &mut argc,
        p,
        std::ptr::null_mut(),
        std::ptr::null_mut(),
    );
    let mut start = 0;
// converting the napi_value to u64 number
    napi_get_value_int64(env, *p, &mut start);
// promise which would be returned
    let mut promise: napi_value = std::mem::zeroed();
// a pointer to promise to resolve is or reject it
    let mut deferred: napi_deferred = std::mem::zeroed();
// a pointer to our async work name used for debugging
    let mut work_name: napi_value = std::mem::zeroed();
// pointer to async work 
    let mut work: napi_async_work = std::mem::zeroed();
    let async_name = CString::new("async fibonaci").expect("Error creating string");
// creating a string for name
    napi_create_string_utf8(env, async_name.as_ptr(), 13, &mut work_name);
// creating a promise
    napi_create_promise(env, &mut deferred, &mut promise);
    let v = Data {
        deferred,
        work,
        val: start as u64,
        result: None,
    };
// creating a context which can be saved to share state between our functions
    let data = Box::new(v);
// converting it to raw pointer
    let raw = Box::into_raw(data);
// creating the work
    napi_create_async_work(
        env,
        std::ptr::null_mut(),
        work_name,
        Some(perform),
        Some(complete),
        std::mem::transmute(raw),
        &mut work,
    );
// queuing to execute the work
    napi_queue_async_work(env, work);
// setting pointer to work that can be used later
    (*raw).work = work;
// retuning the pormise
    promise
}

perform 函数

pub unsafe extern "C" fn perform(_env: napi_env, data: *mut c_void) {
// getting the shared data and converting the in box
    let mut t: Box<Data> = Box::from_raw(std::mem::transmute(data));
    let mut last = 1;
    let mut second_last = 0;
    for _ in 2..t.val {
        let temp = last;
        last = last + second_last;
        second_last = temp;
    }
// setting the result on shared context
    t.result = Some(Ok(last));
// telling the rust to not to drop the context data
    Box::into_raw(t);
}

complete 函数

pub unsafe extern "C" fn complete(env: napi_env, _status: napi_status, data: *mut c_void) {
// getting the shared context
    let t: Box<Data> = Box::from_raw(std::mem::transmute(data));
    let v = match task.result {
        Some(d) => match d {
            Ok(result) => result,
            Err(_) => {
// if there is error just throw an error
// creating error
                let mut js_error: napi_value = std::mem::zeroed();
                napi_create_error(
                    env,
                    std::ptr::null_mut(),
                    std::ptr::null_mut(),
                    &mut js_error,
                );
// rejecting the promise with error
                napi_reject_deferred(env, task.deferred, js_error);
// deleting the task from the queue
                napi_delete_async_work(env, task.work);
                return;
            }
        },
        None => {
// if no result is found reject with error
// creating an error
            let mut js_error: napi_value = std::mem::zeroed();
            napi_create_error(
                env,
                std::ptr::null_mut(),
                std::ptr::null_mut(),
                &mut js_error,
            );
// rejecting promise with error
            napi_reject_deferred(env, task.deferred, js_error);
// deleting the task from queue
            napi_delete_async_work(env, task.work);
            return;
        }
    };
// creating the number
    let mut obj: napi_value = std::mem::zeroed();
    napi_create_int64(env, v as i64, &mut obj);
// resolving the promise with result
    napi_resolve_deferred(env, t.deferred, obj);
// deleting the work
    napi_delete_async_work(env, t.work);
}

当你涉及N-API处理工作时,这只是冰山一角。 我们列出了一些模式并涵盖了基础知识,例如如何导出函数,创建字符串,数值,数组,对象等JavaScript类型,获取函数的上下文,获取参数和参 函数中的this

我们还的深入研究了如何使用libuv线程示例,并在后台中创建async_work以执行密集型计算。 最后,我们创建并使用了JavaScript的promise,并学习了如何在N-API中处理错误。

如果您不想用手写所有代码,有许多库可用。 比如neonnode-bindgennapi-rs。它们都提供了很好的抽象。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK