我想编写一个异步函数,该函数反复从Web轮询资源,并在就绪时返回。我正在使用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
这有效,但是它使用了一个额外的线程来跟踪超时。我认为应该有一个更好的方法。我该如何习惯地实现同一目标?
答案 0 :(得分:1)
由于您使用的是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());
}