Is there any way to create a async stream generator that yields the result of repeatedly calling a function?

懵懂的女人 提交于 2020-01-22 02:21:25

问题


I want to build a program that collects weather updates and represents them as a stream. I want to call get_weather() in an infinite loop, with 60 seconds delay between finish and start.

A simplified version would look like this:

async fn get_weather() -> Weather { /* ... */ }

fn get_weather_stream() -> impl futures::Stream<Item = Weather> {
    loop {
        tokio::timer::delay_for(std::time::Duration::from_secs(60)).await;
        let weather = get_weather().await;
        yield weather; // This is not supported
        // Note: waiting for get_weather() stops the timer and avoids overflows.
    }
}

Is there any way to do this easily?

Using tokio::timer::Interval will not work when get_weather() takes more than 60 seconds:

fn get_weather_stream() -> impl futures::Stream<Item = Weather> {
    tokio::timer::Interval::new_with_delay(std::time::Duration::from_secs(60))
        .then(|| get_weather())
}

If that happens, the next function will start immediately. I want to keep exactly 60 seconds between the previous get_weather() start and the next get_weather() start.


回答1:


Use stream::unfold to go from the "world of futures" to the "world of streams". We don't need any extra state, so we use the empty tuple:

use std::time::Duration;
use tokio::timer;

struct Weather;

async fn get_weather() -> Weather {
    Weather
}

const BETWEEN: Duration = Duration::from_secs(1);

fn get_weather_stream() -> impl futures::Stream<Item = Weather> {
    futures::stream::unfold((), |_| {
        async {
            timer::delay_for(BETWEEN).await;
            let weather = get_weather().await;
            Some((weather, ()))
        }
    })
}

use futures::StreamExt;

#[tokio::main]
async fn main() {
    get_weather_stream()
        .take(3)
        .for_each(|_v| {
            async {
                println!("Got the weather");
            }
        })
        .await;
}
% time ./target/debug/example

Got the weather
Got the weather
Got the weather

real    3.013   3013370us
user    0.004   3763us
sys     0.003   2604us
[dependencies]
futures-preview = "0.3.0-alpha.19"
tokio = "0.2.0-alpha.6"

Tested with rustc 1.39.0-beta.7

See also:

  • How do I convert an iterator into a stream on success or an empty stream on failure?
  • How do I iterate over a Vec of functions returning Futures in Rust?
  • Creating a stream of values while calling async fns?


来源:https://stackoverflow.com/questions/58700741/is-there-any-way-to-create-a-async-stream-generator-that-yields-the-result-of-re

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!