async-await - 我如何编写一个异步函数来轮询资源并在资源准备就绪时返回或在几秒钟内重试?

标签 async-await rust

我想编写一个异步函数,它反复轮询网络资源并在准备就绪时返回。我正在使用 future::poll_fn 实现它:

#![feature(async_await)]

/*
[dependencies]
rand = "0.7.0"
futures-preview = "=0.3.0-alpha.18"
*/

use futures::future;
use rand;
use std::task::Poll;

enum ResourceStatus {
    Ready,
    NotReady,
}
use ResourceStatus::*;

// Mocking the function requesting a web resource
fn poll_web_resource() -> ResourceStatus {
    if rand::random::<f32>() < 0.1 {
        Ready
    } else {
        NotReady
    }
}

async fn async_get_resource() {
    // do other works
    future::poll_fn(|ctx| match poll_web_resource() {
        Ready => Poll::Ready(()),
        NotReady => Poll::Pending,
    })
    .await
}

fn main() {
    futures::executor::block_on(async_get_resource());
}

它不起作用,因为当 poll_web_resource() 返回 NotReady 时,任务将永远停止。解决它的一种方法是在每次返回 Pending 时唤醒任务:

future::poll_fn(|ctx| match poll_web_resource() {
    Ready => Poll::Ready(()),
    NotReady => {
        ctx.waker().wake_by_ref();
        Poll::Pending
    }
})
.await

这会产生大量不必要的请求。对于我的用例,理想情况是在资源未准备好时每隔几秒请求一次资源。这是我目前的解决方法:

future::poll_fn(|ctx| match poll_web_resource() {
    Ready => Poll::Ready(()),
    NotReady => {
        let waker = ctx.waker().clone();
        thread::spawn(move || {
            thread::sleep(Duration.from_millis(5000));
            waker.wake();
        });
        Poll::Pending
    }
})
.await

这可行,但它使用一个额外的线程来跟踪超时。我认为应该有更好的方法来做到这一点。我怎样才能更地道地实现相同的目标?

最佳答案

由于您使用的是 async/await 关键字,请编写一个循环,在资源可用时退出,或在资源不可用时等待。可以使用 Tokio 的 Delay 来完成等待。 :

#![feature(async_await)]

use futures; // 0.3.0-alpha.17
use rand; // 0.7.0
use std::time::Duration;
use tokio::timer; // 0.2.0-alpha.1

enum ResourceStatus {
    Ready,
    NotReady,
}
use ResourceStatus::*;

async fn async_get_resource() {
    const SLEEP_TIME: Duration = Duration::from_secs(1);

    loop {
        match poll_web_resource() {
            Ready => return,
            NotReady => {
                // Don't actually use println in production async code.
                println!("Waiting...");
                timer::Delay::new(tokio::clock::now() + SLEEP_TIME).await;
            }
        }
    }
}

fn poll_web_resource() -> ResourceStatus {
    if rand::random::<f32>() < 0.1 {
        Ready
    } else {
        NotReady
    }
}

fn main() {
    let runtime = tokio::runtime::Runtime::new().expect("Unable to create the runtime");
    let _resource = runtime.block_on(async_get_resource());
}

关于async-await - 我如何编写一个异步函数来轮询资源并在资源准备就绪时返回或在几秒钟内重试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57466422/

相关文章:

javascript - 在异步函数内部,回调函数的返回值返回 Promise(undefined)

javascript - Async/Await 无法按预期使用 Promise.all 和 .map 函数

types - 为什么ncurses-rs中WINDOW的类型用*mut i8?

Rust递归类型难度

struct - 有没有办法在 Rust 中获取 `struct` 的地址?

c# - foreach 中的异步方法并将结果添加到列表中

c# - 如何在 lambda 方法中传递参数和异步关键字?

c# - 选择不在 ASP.NET Core Web API Controller 中等待异步函数运行

rust - 哪里有课!红 bean 杉的宏

opengl - 在 OpenGL 中显示代码生成的纹理