rust-lang / futures-rs

Zero-cost asynchronous programming in Rust
https://rust-lang.github.io/futures-rs/
Apache License 2.0
5.34k stars 616 forks source link

`FutureExt::now_or_never` always returning None when using `futures_timer::Delay` #2745

Open foohyfooh opened 1 year ago

foohyfooh commented 1 year ago

I am trying to write something to wait some time and check the async function is finished so I was using FutureExt::now_or_never but it is giving me unexpected output. The following is the example program I was using to test.

Cargo.toml

[package]
name = "futures-test"
version = "0.1.0"
edition = "2021"

[dependencies]
futures = "0.3.28"
futures-timer = "3.0.2"

main.rs

use std::time::Duration;
use futures::{FutureExt, executor};
use futures_timer::Delay;

async fn wait_for_signal() -> i32 {
    Delay::new(Duration::from_millis(5)).await;
    return 42
}

fn main() {
    let timeout = Duration::from_secs(5);
    let signal_future = wait_for_signal();
    executor::block_on(Delay::new(timeout));
    let signal = signal_future.now_or_never();
    println!("Signal: {:?}", signal);
    // if signal.is_none() {
    //     println!("Timed out");
    // } else {
    //     println!("Got data");
    // }
}

The output I am getting is None even though the delay in wait_for_signal should have already been done.

   Compiling futures-test v0.1.0 (/root/futures-test)
    Finished dev [unoptimized + debuginfo] target(s) in 0.20s
     Running `target/debug/futures-test`
Signal: None

But if I comment out Delay::new(Duration::from_millis(5)).await; in wait_for_signal, it is giving me what I expect.

   Compiling futures-test v0.1.0 (/root/futures-test)
    Finished dev [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/futures-test`
Signal: Some(42)
wishawa commented 1 year ago

See this playground. You might think that the 5 seconds wait started when signal_future was created - in the line

let signal_future = wait_for_signal();

But async code are lazy and do nothing unless polled. The body of wait_for_signal wasn't executed until when the signal_future was polled in the line

let signal = signal_future.now_or_never();
foohyfooh commented 1 year ago

Playground Code Thanks to your playground I noticed something interesting between the handling of sleep vs Delay. now_or_never waits for the future when using sleep for the additional time and returns the value; but doesn't do that with Delay and returns the None. Since playground only gives the output after the whole program is run or halted, you may be required to run the playground code locally to properly see the difference.