选择
选择操作会等待一组 Future 中的任意一个就绪,并对 Future 产生的结果进行响应。在 JavaScript 中,该操作类似于 Promise.race
。在 Python 中,它相当于 asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED)
。
Similar to a match statement, the body of select!
has a number of arms, each of the form pattern = future => statement
. When a future
is ready, its return value is destructured by the pattern
. The statement
is then run with the resulting variables. The statement
result becomes the result of the select!
macro.
use tokio::sync::mpsc::{self, Receiver};
use tokio::time::{sleep, Duration};
#[derive(Debug, PartialEq)]
enum Animal {
Cat { name: String },
Dog { name: String },
}
async fn first_animal_to_finish_race(
mut cat_rcv: Receiver<String>,
mut dog_rcv: Receiver<String>,
) -> Option<Animal> {
tokio::select! {
cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),
dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })
}
}
#[tokio::main]
async fn main() {
let (cat_sender, cat_receiver) = mpsc::channel(32);
let (dog_sender, dog_receiver) = mpsc::channel(32);
tokio::spawn(async move {
sleep(Duration::from_millis(500)).await;
cat_sender.send(String::from("Felix")).await.expect("Failed to send cat.");
});
tokio::spawn(async move {
sleep(Duration::from_millis(50)).await;
dog_sender.send(String::from("Rex")).await.expect("Failed to send dog.");
});
let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
.await
.expect("Failed to receive winner");
println!("Winner is {winner:?}");
}
在本示例中,猫和狗之间进行了一场比赛。
first_animal_to_finish_race
会同时监听这两个通道,并选择最先到达终点的作为胜者。由于狗用时 50 毫秒,而猫用时 500 毫秒,前者在此比赛中大获全胜。在本示例中,可以使用
oneshot
通道,因为这些通道只能接收一次send
信号。尝试为比赛添加截至时间,演示如何选择不同类型的 Future。
请注意,
select!
会丢弃不匹配的分支,相对应的 Future 也会随之取消。最简单的方法是,每次执行select!
时创建新的 Future。- 另一种方法是传递
&mut future
而不是 future 本身,但这可能会导致问题,在本幻灯片的 “固定” 部分进行了详细介绍。
- 另一种方法是传递