napi/bindgen_runtime/js_values/
promise.rs

1use std::{
2  cell::Cell,
3  convert::identity,
4  future,
5  pin::Pin,
6  rc::Rc,
7  task::{Context, Poll},
8};
9
10use futures::channel::oneshot::{channel, Receiver};
11
12use crate::{sys, Error, Result, Status};
13
14use super::{CallbackContext, FromNapiValue, PromiseRaw, TypeName, Unknown, ValidateNapiValue};
15
16/// The JavaScript Promise object representation
17///
18/// This `Promise<T>` can be awaited in the Rust
19/// This `Promise<T>` can also be passed from `#[napi]` fn
20///
21/// example:
22///
23/// ```no_run
24/// #[napi]
25/// pub fn await_promise_in_rust(promise: Promise<u32>) {
26///   let value = promise.await.unwrap();
27///
28///   println!("{value}");
29/// }
30/// ```
31///
32/// But this `Promise<T>` can not be pass back to `JavaScript`.
33/// If you want to use raw JavaScript `Promise` API, you can use the [`PromiseRaw`](./PromiseRaw) instead.
34pub struct Promise<T: 'static + FromNapiValue> {
35  value: Pin<Box<Receiver<Result<T>>>>,
36}
37
38impl<T: FromNapiValue> TypeName for Promise<T> {
39  fn type_name() -> &'static str {
40    "Promise"
41  }
42
43  fn value_type() -> crate::ValueType {
44    crate::ValueType::Object
45  }
46}
47
48impl<T: FromNapiValue> ValidateNapiValue for Promise<T> {
49  unsafe fn validate(
50    env: crate::sys::napi_env,
51    napi_val: crate::sys::napi_value,
52  ) -> Result<sys::napi_value> {
53    use super::validate_promise;
54
55    validate_promise(env, napi_val)
56  }
57}
58
59unsafe impl<T: FromNapiValue + Send> Send for Promise<T> {}
60
61impl<T: FromNapiValue> FromNapiValue for Promise<T> {
62  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> crate::Result<Self> {
63    let (tx, rx) = channel();
64    let promise_object = unsafe { PromiseRaw::<T>::from_napi_value(env, napi_val)? };
65    let tx_box = Rc::new(Cell::new(Some(tx)));
66    let tx_in_catch = tx_box.clone();
67    promise_object
68      .then(move |ctx| {
69        if let Some(sender) = tx_box.replace(None) {
70          // no need to handle the send error here, the receiver has been dropped
71          let _ = sender.send(Ok(ctx.value));
72        }
73        Ok(())
74      })?
75      .catch(move |ctx: CallbackContext<Unknown>| {
76        if let Some(sender) = tx_in_catch.replace(None) {
77          // no need to handle the send error here, the receiver has been dropped
78          let _ = sender.send(Err(ctx.value.into()));
79        }
80        Ok(())
81      })?;
82
83    Ok(Promise {
84      value: Box::pin(rx),
85    })
86  }
87}
88
89impl<T: FromNapiValue> future::Future for Promise<T> {
90  type Output = Result<T>;
91
92  fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
93    match self.value.as_mut().poll(cx) {
94      Poll::Pending => Poll::Pending,
95      Poll::Ready(v) => Poll::Ready(
96        v.map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))
97          .and_then(identity),
98      ),
99    }
100  }
101}