選取
選取作業會等到任何一組 Future 準備就緒,再針對該 Future 的結果提供回應。這類似於 JavaScript 中的 Promise.race
。在 Python 中,則可與 asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED)
比較。
類似於比對陳述式,select!
的主體有多個分支,格式皆為 pattern = future => statement
。當 future
準備就緒時,回傳值會由 pattern
解構。接著,statement
執行時會利用產生的變數。statement
結果會成為 select!
巨集的結果。
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 本身,但這可能導致多項問題,詳細討論請見 pinning 投影片。
- 替代方法是傳遞