use deno_core::{
v8::{self, PromiseState},
PollEventLoopOptions,
};
use serde::Deserialize;
use super::V8Value;
use crate::{async_bridge::AsyncBridgeExt, Error};
#[derive(Eq, Hash, PartialEq, Debug, Clone)]
pub struct Promise<T>(V8Value<PromiseTypeChecker>, std::marker::PhantomData<T>)
where
T: serde::de::DeserializeOwned;
impl_v8!(Promise<T>, PromiseTypeChecker);
impl_checker!(PromiseTypeChecker, Promise, is_promise, |e| {
crate::Error::JsonDecode(format!("Expected a promise, found `{e}`"))
});
impl<T> Promise<T>
where
T: serde::de::DeserializeOwned,
{
pub(crate) async fn resolve(
self,
runtime: &mut deno_core::JsRuntime,
) -> Result<T, crate::Error> {
let future = runtime.resolve(self.0 .0);
let result = runtime
.with_event_loop_future(future, PollEventLoopOptions::default())
.await?;
let mut scope = runtime.handle_scope();
let local = v8::Local::new(&mut scope, &result);
Ok(deno_core::serde_v8::from_v8(&mut scope, local)?)
}
pub async fn into_future(self, runtime: &mut crate::Runtime) -> Result<T, crate::Error> {
self.resolve(runtime.deno_runtime()).await
}
pub fn into_value(self, runtime: &mut crate::Runtime) -> Result<T, crate::Error> {
runtime.block_on(move |runtime| async move { self.into_future(runtime).await })
}
pub fn is_pending(&self, runtime: &mut crate::Runtime) -> bool {
let mut scope = runtime.deno_runtime().handle_scope();
let value = self.0.as_local(&mut scope);
value.state() == v8::PromiseState::Pending
}
pub fn poll_promise(&self, runtime: &mut crate::Runtime) -> std::task::Poll<Result<T, Error>> {
let mut scope = runtime.deno_runtime().handle_scope();
let value = self.0.as_local(&mut scope);
match value.state() {
PromiseState::Pending => std::task::Poll::Pending,
PromiseState::Rejected => {
let error = value.result(&mut scope);
let error = deno_core::error::JsError::from_v8_exception(&mut scope, error);
std::task::Poll::Ready(Err(error.into()))
}
PromiseState::Fulfilled => {
let result = value.result(&mut scope);
match deno_core::serde_v8::from_v8::<T>(&mut scope, result) {
Ok(value) => std::task::Poll::Ready(Ok(value)),
Err(e) => std::task::Poll::Ready(Err(e.into())),
}
}
}
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{js_value::Function, json_args, Module, Runtime, RuntimeOptions};
#[test]
fn test_promise() {
let module = Module::new(
"test.js",
"
export const f = () => new Promise((resolve) => resolve(42));
",
);
let mut runtime = Runtime::new(RuntimeOptions::default()).unwrap();
let handle = runtime.load_module(&module).unwrap();
let f: Function = runtime.get_value(Some(&handle), "f").unwrap();
let value: Promise<usize> = f
.call_immediate(&mut runtime, Some(&handle), &json_args!())
.unwrap();
let value = value.into_value(&mut runtime).unwrap();
assert_eq!(value, 42);
}
}