#![cfg_attr(target_feature = "atomics", feature(stdsimd))]
#![deny(missing_docs)]
use js_sys::Promise;
use std::cell::RefCell;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::rc::Rc;
use std::task::{Context, Poll, Waker};
use wasm_bindgen::prelude::*;
mod queue;
#[cfg(feature = "futures-core-03-stream")]
pub mod stream;
mod task {
use cfg_if::cfg_if;
cfg_if! {
if #[cfg(target_feature = "atomics")] {
mod wait_async_polyfill;
mod multithread;
pub(crate) use multithread::*;
} else {
mod singlethread;
pub(crate) use singlethread::*;
}
}
}
#[inline]
pub fn spawn_local<F>(future: F)
where
F: Future<Output = ()> + 'static,
{
task::Task::spawn(Box::pin(future));
}
struct Inner {
result: Option<Result<JsValue, JsValue>>,
task: Option<Waker>,
callbacks: Option<(Closure<dyn FnMut(JsValue)>, Closure<dyn FnMut(JsValue)>)>,
}
pub struct JsFuture {
inner: Rc<RefCell<Inner>>,
}
impl fmt::Debug for JsFuture {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "JsFuture {{ ... }}")
}
}
impl From<Promise> for JsFuture {
fn from(js: Promise) -> JsFuture {
let state = Rc::new(RefCell::new(Inner {
result: None,
task: None,
callbacks: None,
}));
fn finish(state: &RefCell<Inner>, val: Result<JsValue, JsValue>) {
let task = {
let mut state = state.borrow_mut();
debug_assert!(state.callbacks.is_some());
debug_assert!(state.result.is_none());
drop(state.callbacks.take());
state.result = Some(val);
state.task.take()
};
if let Some(task) = task {
task.wake()
}
}
let resolve = {
let state = state.clone();
Closure::once(move |val| finish(&state, Ok(val)))
};
let reject = {
let state = state.clone();
Closure::once(move |val| finish(&state, Err(val)))
};
let _ = js.then2(&resolve, &reject);
state.borrow_mut().callbacks = Some((resolve, reject));
JsFuture { inner: state }
}
}
impl Future for JsFuture {
type Output = Result<JsValue, JsValue>;
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
let mut inner = self.inner.borrow_mut();
if let Some(val) = inner.result.take() {
return Poll::Ready(val);
}
inner.task = Some(cx.waker().clone());
Poll::Pending
}
}
pub fn future_to_promise<F>(future: F) -> Promise
where
F: Future<Output = Result<JsValue, JsValue>> + 'static,
{
let mut future = Some(future);
Promise::new(&mut |resolve, reject| {
let future = future.take().unwrap_throw();
spawn_local(async move {
match future.await {
Ok(val) => {
resolve.call1(&JsValue::undefined(), &val).unwrap_throw();
}
Err(val) => {
reject.call1(&JsValue::undefined(), &val).unwrap_throw();
}
}
});
})
}