我想编写一个异步函数,该函数反复从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
这有效,但它使用一个额外的线程来跟踪超时。我认为应该有更好的方法。我怎样才能更习惯地实现相同的目标?
由于您使用的是async
/await
关键字,因此请编写一个循环,该循环在资源可用时退出,或在资源不可用时等待。 等待可以通过东京的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());
}