There are some crates around for using JavaScript timers, but I want a cross-platform way that uses wasm_bindgen
for the browser (plus js_sys
, web_sys
and wasm_bindgen_futures
and proper animation intervals) and fits with tokio
, so that I have a solid abstraction layer over both. Anyway, I think this is trouble:
(Like tokio::time::timeout
, but for the browser.)
rialight_util::timing
internal submodule: platform::browser_runtime
pub async fn timeout<F: Future + Send + 'static>(duration: Duration, future: F) -> Result<(), super::ElapsedError> {
let mut completed = Arc::new(RwLock::new(false));
super::exec_future({
let completed = Arc::clone(&mut completed);
async move {
future.await;
*completed.write().unwrap() = true;
}
});
if *completed.read().unwrap() {
return Ok(());
}
todo!();
wait(duration).await;
Err(super::ElapsedError)
}
Any recommendation for what to do?
Full source:
pub async fn timeout<F: Future<Output = ()> + Send + 'static>(duration: Duration, future: F) -> Result<(), super::ElapsedError> {
let (_, i) = future_race([
async { future.await; },
async { wait(duration).await; },
]).await;
match i {
0 => Ok(()),
1 => Err(super::ElapsedError),
}
}
#[derive(Debug)]
pub struct Interval {
pub for_animation: bool,
pub period: Duration,
pub start: super::SuperInstant,
pub ticker: Option<Ticker>,
}
The wait
is implemented for instance, but it's based on a promise and I'm not sure how to poll properly before expiration
I got this idea:
#[derive(Debug)]
struct Timeout<F: Future + Send + 'static> {
pub operation: wasm_bindgen_futures::JsFuture,
pub resolving_to: F,
pub expired: Arc<RwLock<bool>>,
}
impl<F: Future + Send + 'static> Future for Timeout<F> {
type Output = Result<(), super::ElapsedError>;
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
if *self.expired.read().unwrap() {
std::pin::pin!(self.resolving_to).poll()
} else {
std::pin::pin!(self.resolving_to).poll()
}
}
}
Any idea on how to poll with a value?
This doesn't make sense I'd like to race by the way...
I'm getting this strange error:
error[E0308]: mismatched types
--> src\util\src\timing\platform_based\browser_runtime.rs:128:9
|
127 | (async { future.await; }),
| ----------------------- the expected `async` block
128 | (async { wait(duration).await; }),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `async` block, found a different `async` block
|
= note: expected `async` block `[async block@src\util\src\timing\platform_based\browser_runtime.rs:127:10: 127:33]`
found `async` block `[async block@src\util\src\timing\platform_based\browser_runtime.rs:128:10: 128:41]`
Code:
pub async fn timeout<F: Future<Output = ()> + Send + 'static>(duration: Duration, future: F) -> Result<(), super::ElapsedError> {
let (_, i) = future_race([
(async { future.await; }),
(async { wait(duration).await; }),
]).await;
match i {
0 => Ok(()),
1 => Err(super::ElapsedError),
}
}
You're trying to pass an array of futures, but the concrete type of every async block is a unique opaque type. You need type erasure of some sort to store multiple async blocks in an array
1 Like
I got it... The problem is that I'm using wasm_bindgen_futures::JsFuture
to convert a JavaScript promise to a Rust future.
JsFuture
doesn't implement Send
, therefore the wait(...)
call has no compatibility with the timeout future...
If there were a way to remove that Send
bound from tokio::time::timeout
's given future too, it might be easy to get this working.
It looks like I got no compilation error when removing Send
from given future here:
pub async fn timeout<F>(duration: Duration, future: F) -> Result<(), ElapsedError>
where
F: Future<Output = ()> + 'static,
{
#[cfg(feature = "rialight_default_export")] {
return match tokio::time::timeout(duration, future).await {
Err(_) => Err(ElapsedError),
Ok(_) => Ok(()),
};
}
#[cfg(feature = "rialight_browser_export")] {
todo!();
}
#[cfg(not(any(feature = "rialight_default_export", feature = "rialight_browser_export")))] {
let _ = (duration, future);
panic!("Incorrectly configured Rialight runtime");
}
}
However the issue above has not been resolved yet even removing Send
.
I get it though when you mean "opaque". Even removing the Send
bounds, these futures are still of separate type, right? Hmm