diff --git a/packages/yew/src/platform/rt_wasm_bindgen/time.rs b/packages/yew/src/platform/rt_wasm_bindgen/time.rs index 99206d354af..c34dea6099c 100644 --- a/packages/yew/src/platform/rt_wasm_bindgen/time.rs +++ b/packages/yew/src/platform/rt_wasm_bindgen/time.rs @@ -1,53 +1,68 @@ +use std::cell::Cell; use std::future::Future; use std::pin::Pin; +use std::rc::Rc; use std::task::{Context, Poll}; use std::time::Duration; use futures::stream; use futures::stream::Stream; use gloo::timers::callback::Timeout; -use wasm_bindgen::UnwrapThrowExt; #[inline(always)] pub(crate) fn sleep(dur: Duration) -> impl Future { pub struct Sleep { inner: Option, - dur_millis: Option, + dur_left: Option, + timeout_registered: Rc>, } impl Future for Sleep { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - // set_timeout can only accept maximum of i32, so we wrap around if it gets longer. static I32_MAX_U128: u128 = 2_147_483_647; static I32_MAX_U32: u32 = 2_147_483_647; - match self.dur_millis.as_mut() { - Some(m) if *m > I32_MAX_U128 => { - *m -= I32_MAX_U128; - let waker = cx.waker().clone(); - self.inner = Some(Timeout::new(I32_MAX_U32, move || { - waker.wake(); - })); - Poll::Pending + // If polling before the registered timeout is reached, return Pending. + if self.timeout_registered.get() { + return Poll::Pending; + } + + // set_timeout can only accept maximum of i32, so we wrap around if it gets longer. + let next_timeout = match self.dur_left.map(|m| (m, u32::try_from(m))) { + Some((m_u128, Err(_))) => { + self.dur_left = Some(m_u128 - I32_MAX_U128); + I32_MAX_U32 } - Some(m) => { - let waker = cx.waker().clone(); - self.inner = Some(Timeout::new((*m).try_into().unwrap_throw(), move || { - waker.wake(); - })); - self.dur_millis = None; - Poll::Pending + Some((m_u128, _)) if m_u128 > I32_MAX_U128 => { + self.dur_left = Some(m_u128 - I32_MAX_U128); + I32_MAX_U32 } - None => Poll::Ready(()), - } + Some((_, Ok(m_u32))) => { + self.dur_left = None; + m_u32 + } + None => return Poll::Ready(()), + }; + + let waker = cx.waker().clone(); + self.timeout_registered.set(true); + let timeout_registered = self.timeout_registered.clone(); + + self.inner = Some(Timeout::new(next_timeout, move || { + timeout_registered.set(false); + waker.wake(); + })); + + Poll::Pending } } Sleep { inner: None, - dur_millis: Some(dur.as_millis()), + dur_left: Some(dur.as_millis()), + timeout_registered: Cell::new(false).into(), } }