1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
use crate::{api, prelude::*};
use std::{marker::PhantomData, mem::MaybeUninit};
#[derive(Clone, Debug)]
pub struct NapiAsyncContext {
env: NapiEnv,
context: napi_async_context,
}
impl NapiAsyncContext {
pub(crate) fn from_raw(env: NapiEnv, context: napi_async_context) -> NapiAsyncContext {
NapiAsyncContext { env, context }
}
pub fn env(&self) -> NapiEnv {
self.env
}
pub fn raw(&self) -> napi_async_context {
self.context
}
/// The async_resource object needs to be kept alive until napi_async_destroy to keep async_hooks related API acts correctly. In order to retain ABI compatibility with previous versions, napi_async_contexts are not maintaining the strong reference to the async_resource objects to avoid introducing causing memory leaks. However, if the async_resource is garbage collected by JavaScript engine before the napi_async_context was destroyed by napi_async_destroy, calling napi_async_context related APIs like napi_open_callback_scope and napi_make_callback can cause problems like loss of async context when using the AsyncLocalStorage API.
/// In order to retain ABI compatibility with previous versions, passing NULL for async_resource does not result in an error. However, this is not recommended as this will result poor results with async_hooks init hooks and async_hooks.executionAsyncResource() as the resource is now required by the underlying async_hooks implementation in order to provide the linkage between async callbacks.
pub fn new(env: NapiEnv, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
let context = napi_call!(
=napi_async_init,
env,
env.object()?.raw(),
env.string(name)?.raw(),
);
Ok(NapiAsyncContext { env, context })
}
/// This API can be called even if there is a pending JavaScript exception.
pub fn destroy(&mut self) -> NapiResult<()> {
napi_call!(napi_async_destroy, self.env(), self.raw());
Ok(())
}
/// This method allows a JavaScript function object to be called from a native add-on.
/// This API is similar to napi_call_function. However, it is used to call from native
/// code back into JavaScript after returning from an async operation (when there is no
/// other script on the stack). It is a fairly simple wrapper around node::MakeCallback.
///
/// Note it is not necessary to use napi_make_callback from within a napi_async_complete_callback;
/// in that situation the callback's async context has already been set up, so a direct call to
/// napi_call_function is sufficient and appropriate. Use of the napi_make_callback function may
/// be required when implementing custom async behavior that does not use napi_create_async_work.
///
/// Any process.nextTicks or Promises scheduled on the microtask queue by JavaScript during
/// he callback are ran before returning back to C/C++.
pub fn make_callback<R, T, const N: usize>(
&self,
this: JsObject,
func: Function<R>,
args: [T; N],
) -> NapiResult<R>
where
R: NapiValueT,
T: NapiValueT + Clone,
{
let env = self.env();
let value = napi_call!(
=napi_make_callback,
self.env(),
self.raw(),
this.raw(),
func.raw(),
args.len(),
args.map(|arg| arg.raw()).as_ptr(),
);
Ok(R::from_raw(env, value))
}
#[cfg(feature = "v3")]
/// There are cases (for example, resolving promises) where it is necessary to have the
/// equivalent of the scope associated with a callback in place when making certain
/// Node-API calls. If there is no other script on the stack the napi_open_callback_scope
/// and napi_close_callback_scope functions can be used to open/close the required scope.
pub fn scope(&self) -> NapiResult<NapiCallbackScope> {
let env = self.env();
let scope = napi_call!(
=napi_open_callback_scope,
env,
env.object()?.raw(),
self.raw(),
);
Ok(NapiCallbackScope::from_raw(env, scope))
}
}
impl Drop for NapiAsyncContext {
fn drop(&mut self) {
self.destroy();
}
}