nodex_api/
context.rs

1use crate::{api, prelude::*};
2use std::{marker::PhantomData, mem::MaybeUninit};
3
4#[derive(Clone, Debug)]
5pub struct NapiAsyncContext {
6    env: NapiEnv,
7    context: napi_async_context,
8}
9
10impl NapiAsyncContext {
11    pub(crate) fn from_raw(env: NapiEnv, context: napi_async_context) -> NapiAsyncContext {
12        NapiAsyncContext { env, context }
13    }
14
15    pub fn env(&self) -> NapiEnv {
16        self.env
17    }
18
19    pub fn raw(&self) -> napi_async_context {
20        self.context
21    }
22
23    /// 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.
24    /// 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.
25    pub fn new(env: NapiEnv, name: impl AsRef<str>) -> NapiResult<NapiAsyncContext> {
26        let context = napi_call!(
27            =napi_async_init,
28            env,
29            env.object()?.raw(),
30            env.string(name)?.raw(),
31        );
32
33        Ok(NapiAsyncContext { env, context })
34    }
35
36    /// This API can be called even if there is a pending JavaScript exception.
37    pub fn destroy(&mut self) -> NapiResult<()> {
38        napi_call!(napi_async_destroy, self.env(), self.raw())
39    }
40
41    /// This method allows a JavaScript function object to be called from a native add-on.
42    /// This API is similar to napi_call_function. However, it is used to call from native
43    /// code back into JavaScript after returning from an async operation (when there is no
44    /// other script on the stack). It is a fairly simple wrapper around node::MakeCallback.
45    ///
46    /// Note it is not necessary to use napi_make_callback from within a napi_async_complete_callback;
47    /// in that situation the callback's async context has already been set up, so a direct call to
48    /// napi_call_function is sufficient and appropriate. Use of the napi_make_callback function may
49    /// be required when implementing custom async behavior that does not use napi_create_async_work.
50    ///
51    /// Any process.nextTicks or Promises scheduled on the microtask queue by JavaScript during
52    /// he callback are ran before returning back to C/C++.
53    pub fn make_callback<R, T>(&self, this: JsObject, func: Function<R>, args: T) -> NapiResult<R>
54    where
55        R: NapiValueT,
56        T: ToJsArgs,
57    {
58        let env = self.env();
59        let args = args
60            .to_js_args(env)?
61            .0
62            .into_iter()
63            .map(|value| value.raw())
64            .collect::<Vec<_>>();
65
66        let value = napi_call!(
67            =napi_make_callback,
68            self.env(),
69            self.raw(),
70            this.raw(),
71            func.raw(),
72            T::len(),
73            args.as_ptr(),
74        );
75
76        Ok(R::from_raw(env, value))
77    }
78
79    #[cfg(feature = "v3")]
80    /// There are cases (for example, resolving promises) where it is necessary to have the
81    /// equivalent of the scope associated with a callback in place when making certain
82    /// Node-API calls. If there is no other script on the stack the napi_open_callback_scope
83    /// and napi_close_callback_scope functions can be used to open/close the required scope.
84    pub fn scope(&self) -> NapiResult<NapiCallbackScope> {
85        let env = self.env();
86        let scope = napi_call!(
87            =napi_open_callback_scope,
88            env,
89            env.object()?.raw(),
90            self.raw(),
91        );
92        Ok(NapiCallbackScope::from_raw(env, scope))
93    }
94}
95
96impl Drop for NapiAsyncContext {
97    fn drop(&mut self) {
98        self.destroy();
99    }
100}