124 lines
3.2 KiB
Rust
124 lines
3.2 KiB
Rust
use crate::{ContextRef, ValueRef, ValueResult};
|
|
use std::sync::atomic::{AtomicU64, Ordering};
|
|
use std::sync::mpsc::Sender;
|
|
|
|
#[derive(Eq, PartialEq, Hash, Debug, Clone, Copy)]
|
|
pub(crate) struct PromiseHandle(u64);
|
|
|
|
impl PromiseHandle {
|
|
pub fn new() -> Self {
|
|
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
|
PromiseHandle(NEXT_ID.fetch_add(1, Ordering::SeqCst))
|
|
}
|
|
}
|
|
|
|
pub type PromiseResult = Box<dyn FnOnce(&ContextRef) -> ValueResult + Send + 'static>;
|
|
|
|
pub(crate) enum PromiseEvent {
|
|
Resolved(PromiseResult),
|
|
Rejected(PromiseResult),
|
|
}
|
|
|
|
/// A Promise is a small, thread-safe marker which represents a pending
|
|
/// promise inside the JS runtime. This is how you complete the promise: you
|
|
/// must either call `resolve` or `reject` before you drop the promise. If
|
|
/// you drop the promise without calling either `resolve` or `reject` then it
|
|
/// will panic.
|
|
///
|
|
/// In order to actually *process* the completion, someone must call
|
|
/// process_all_jobs on a context or the underlying runtime itself.
|
|
#[derive(Debug)]
|
|
pub struct Promise {
|
|
complete: bool,
|
|
handle: PromiseHandle,
|
|
channel: Sender<(PromiseHandle, PromiseEvent)>,
|
|
}
|
|
|
|
impl Promise {
|
|
pub(crate) fn new(
|
|
handle: PromiseHandle,
|
|
channel: Sender<(PromiseHandle, PromiseEvent)>,
|
|
) -> Self {
|
|
Promise {
|
|
complete: false,
|
|
handle,
|
|
channel,
|
|
}
|
|
}
|
|
|
|
pub fn resolve<T>(mut self, value: T)
|
|
where
|
|
T: FnOnce(&ContextRef) -> ValueResult + Send + 'static,
|
|
{
|
|
let _ = self
|
|
.channel
|
|
.send((self.handle, PromiseEvent::Resolved(Box::new(value))));
|
|
self.complete = true;
|
|
}
|
|
|
|
pub fn reject<T>(mut self, value: T)
|
|
where
|
|
T: FnOnce(&ContextRef) -> ValueResult + Send + 'static,
|
|
{
|
|
let _ = self
|
|
.channel
|
|
.send((self.handle, PromiseEvent::Rejected(Box::new(value))));
|
|
self.complete = true;
|
|
}
|
|
}
|
|
|
|
impl Drop for Promise {
|
|
fn drop(&mut self) {
|
|
assert!(self.complete);
|
|
}
|
|
}
|
|
|
|
pub trait RejectedPromiseTracker {
|
|
fn on_rejected_promise(
|
|
&self,
|
|
ctx: &ContextRef,
|
|
promise: &ValueRef,
|
|
reason: &ValueRef,
|
|
is_handled: bool,
|
|
);
|
|
}
|
|
|
|
impl<T> RejectedPromiseTracker for T
|
|
where
|
|
T: Fn(&ContextRef, &ValueRef, &ValueRef, bool) -> (),
|
|
{
|
|
fn on_rejected_promise(
|
|
&self,
|
|
ctx: &ContextRef,
|
|
promise: &ValueRef,
|
|
reason: &ValueRef,
|
|
is_handled: bool,
|
|
) {
|
|
self(ctx, promise, reason, is_handled);
|
|
}
|
|
}
|
|
|
|
pub struct DefaultRejectedPromiseTracker {}
|
|
|
|
impl DefaultRejectedPromiseTracker {
|
|
pub fn new() -> Self {
|
|
DefaultRejectedPromiseTracker {}
|
|
}
|
|
}
|
|
|
|
impl RejectedPromiseTracker for DefaultRejectedPromiseTracker {
|
|
fn on_rejected_promise(
|
|
&self,
|
|
ctx: &ContextRef,
|
|
_promise: &ValueRef,
|
|
reason: &ValueRef,
|
|
is_handled: bool,
|
|
) {
|
|
if !is_handled {
|
|
let reason_str = reason.to_string(ctx).expect(
|
|
"Unhandled rejected promise: reason unknown: unable to convert reason to string",
|
|
);
|
|
panic!("Unhandled rejected promise: {reason_str}");
|
|
}
|
|
}
|
|
}
|