sweet_web/dom_utils/
future_timeout.rs1use crate::timeout_reject;
2use anyhow::Result;
3use js_sys::Array;
4use js_sys::Promise;
5use std::cell::RefCell;
6use std::future::Future;
7use std::rc::Rc;
8use std::time::Duration;
9use wasm_bindgen::JsValue;
10use wasm_bindgen::UnwrapThrowExt;
11use wasm_bindgen_futures::spawn_local;
12use wasm_bindgen_futures::JsFuture;
13
14
15pub async fn future_timeout<F, Fut, O>(fut: F, duration: Duration) -> Result<O>
16where
17 F: 'static + FnOnce() -> Fut,
18 Fut: Future<Output = O>,
19 O: 'static,
20{
21 let out = Rc::<RefCell<Option<O>>>::default();
22
23 let mut fut = Some(fut);
24 let out2 = out.clone();
25 let prom = Promise::new(&mut move |resolve, _reject| {
26 let fut = fut.take().unwrap_throw();
27 let out = out2.clone();
28 spawn_local(async move {
29 let result = fut().await;
30 *out.borrow_mut() = Some(result);
31 resolve.call0(&JsValue::NULL).unwrap();
32 });
33 });
34 let timeout = timeout_reject(duration);
35
36 let arr = Array::new();
37 arr.push(&prom);
38 arr.push(&timeout);
39
40 match JsFuture::from(Promise::race(&arr)).await {
41 Ok(_) => Ok(out.take().unwrap()),
42 Err(_) => Err(anyhow::anyhow!("Timeout")),
43 }
44}
45
46
47#[cfg(test)]
48#[cfg(target_arch = "wasm32")]
49mod test {
50 use crate::prelude::*;
51 use std::time::Duration;
52 use sweet_test as sweet;
53 use sweet_test::prelude::*;
54
55 #[sweet_test::test]
56 pub async fn works() {
57 let a = future_timeout(
58 || async {
59 wait_for_millis(400).await;
60 39
61 },
62 Duration::from_millis(500),
63 )
64 .await
65 .unwrap();
66 expect(a).to_be(39);
67 }
68 #[sweet_test::test]
69 pub async fn times_out() {
70 let err = future_timeout(
71 || async {
72 wait_for_millis(600).await;
73 },
74 Duration::from_millis(500),
75 )
76 .await;
77 expect(err).to_be_err_str("Timeout");
78 }
79}