How to poll a future once and check if it isn't ready before awaiting it?
I know there is once_or_never but that unfortunately consumes the future and if it wasn't ready, then the result is lost.
Something like:
let fut = call_some_async_fn();
if !fut.is_ready() { // poll the future once
println!("will have to wait...");
}
fut.await
You can use now_or_never without consuming it like this:
let fut = call_some_async_fn();
tokio::pin!(fut);
if let Some(output) = fut.as_mut().now_or_never() {
println!("got output {:?}", output);
} else {
fut.await;
}
The way this works is that tokio::pin! creates a new fut variable of type Pin<&mut FutType>, and Pin provides an .as_mut() method that returns a sub-reference that you can consume without consuming the original Pin.
For futures that don't need to be pinned (these are pretty rare - mostly they come from StreamExt), you can also just use a mutable reference like this: (&mut fut).now_or_never()
Warning: This doesn't consume the future even if it returns Some. Awaiting or otherwise polling it again after completion will lead to a panic.
I ended up with a much more complex solution trying to get a std::task::Context to be able to invoke std::future::Future::poll somehow. Meanwhile @alice posted a much better solution, but here's my (non-idiomatic) result anyway:
use std::future::Future;
use std::marker::Unpin;
use std::pin::Pin;
use std::task::{Context, Poll};
pub struct PolledFuture<T>(Option<T>);
impl<T> PolledFuture<T> {
pub fn new(future: T) -> Self {
Self(Some(future))
}
}
impl<T, O> Future for PolledFuture<T>
where
T: Future<Output = O> + Unpin,
{
type Output = Result<O, Self>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<O, Self>> {
let mut inner = self.0.take().unwrap();
let poll = Pin::new(&mut inner).poll(cx);
Poll::Ready(
match poll {
Poll::Ready(x) => Ok(x),
Poll::Pending => Err(PolledFuture(Some(inner))),
}
)
}
}
#[tokio::main]
async fn main() {
let f = tokio::time::sleep(std::time::Duration::from_millis(100));
let polled_f = PolledFuture::new(Box::pin(f));
let polled_f = match polled_f.await {
Ok(()) => {
println!("Completed already.");
return;
}
Err(fut) => {
println!("Not completed yet.");
fut
}
};
std::thread::sleep(std::time::Duration::from_millis(200));
let polled_f = match polled_f.await {
Ok(()) => {
println!("Completed finally.");
return;
}
Err(fut) => {
println!("Still not completed.");
fut
}
};
drop(polled_f);
}
In that code, I didn't know about std::pin::Pin::as_mut yet. Hency my clumsy approach of returning a new future as a Result::Err for the case where the inner future didn't resolve.