Env
From Node.js documents
napi_env
is used to represent a context that the underlying N-API implementation can use to persist VM-specific state. This structure is passed to native functions when they’re invoked, and it must be passed back when making N-API calls. Specifically, the samenapi_env
that was passed in when the initial native function was called must be passed to any subsequent nested N-API calls. Caching thenapi_env
for the purpose of general reuse, and passing thenapi_env
between instances of the same addon running on different Worker threads is not allowed. Thenapi_env
becomes invalid when an instance of a native addon is unloaded. Notification of this event is delivered through the callbacks given tonapi_add_env_cleanup_hook
andnapi_set_instance_data
.
Env
is a Rust
layer abstraction over napi_env
. When writing Node.js Add-ons in C/C++
, any N-API call goes through napi_env
, and similarly in Rust
, any API call goes through the Env
data structure. In napi-rs
, there are four ways to interact with Env
.
1. CallContext in js_function
js_function
is a way to define a JavaScript function
in Rust
:
#[js_function(1)]
fn hello(ctx: CallContext) -> Result<JsString> {
let argument_one = ctx.get::<JsString>(0)?.into_utf8()?;
ctx.env.create_string_from_std(format!("{} world!", argument_one.as_str()?))
}
There is env
field in CallContext
struct.
Env
functionality can be found in the Env documentation.
2. contextless_function
contextless_function
is very similar to js_function
, except that contextless_function
will not get the Context of this JavaScript function
.
Which means in contextless_function
you can not get arguments, this or any other function context relational data. But it will more perform more efficiently so it can be used in performance sensitive scenarios.
#[contextless_function]
fn just_return_hello(env: Env) -> ContextlessResult<JsString> {
env.create_string("hello").map(Some)
}
3. resolve/reject
method in Task trait
The Task trait is a little complicated, see it’s docs for a dive deep into it.
Env
will be passed into your resolve
and reject
methods. The two methods will be called in main thread when async task in the thread pool was completed.
struct PlusOneAsync(u32);
impl Task for PlusOneAsync {
type Output = u32;
type JsValue = JsNumber;
fn compute(&mut self) -> Result<Self::Output> {
Ok(self.0 + 1)
}
fn resolve(self, env: Env, output: Self::Output) -> Result<Self::JsValue> {
env.create_uint32(output)
}
}
#[js_function(1)]
fn async_plus_one(ctx: CallContext) -> Result<JsNumber> {
let input_number: u32 = ctx.get::<JsNumber>(0)?.try_into()?;
let task = PlusOneAsync(input_number);
ctx.env.spawn(task).map(|t| t.promise_object())
}
asyncPlusOne(1).then((result) => {
console.log(result) // 2
})
4. ThreadSafeCallContext
in Thread safe function
#[js_function(1)]
fn thread_safe_function(ctx: CallContext) -> Result<JsUndefined> {
let callback: JsFunction = ctx.get(0)?;
let tsfn = ctx.create_threadsafe_function(&callback, 0, |ctx: ThreadSafeCallContext<u32>| {
ctx.env.create_uint32(ctx.value).map(|js_value| vec![js_value])
})?;
std::thread::spawn(move || {
tsfn.call(Ok(1), ThreadsafeFunctionCallMode::NonBlocking);
});
ctx.env.get_undefined()
}
threadSafeFunction((err, value) => {
if (err) {
console.error(err)
process.exit(1)
}
console.log(value) // 1
})