async
/.await
入門
async/.await
是 Rust 內建編寫非同步函式的工具,讓非同步函式寫起來像同步。async
會將一塊程式碼轉換為一個實作 future
trait 的狀態機。相較於在同步的方法中呼叫一個阻塞函式(blocking function)會阻塞整個執行緒,反之,被阻塞的 future
會釋出執行緒的控制權,讓其他 future
可以繼續運作。
使用 async fn
語法來建立一個非同步函式:
# #![allow(unused_variables)] #fn main() { async fn do_something() { ... } #}
async fn
的返回值是一個 future
。要讓事情發生,future
需要在一個執行器(executor)上面運作。
// `block_on` blocks the current thread until the provided future has run to // completion. Other executors provide more complex behavior, like scheduling // multiple futures onto the same thread. use futures::executor::block_on; async fn hello_world() { println!("hello, world!"); } fn main() { let future = hello_world(); // Nothing is printed block_on(future); // `future` is run and "hello, world!" is printed }
在一個 async fn
裡,可以使用 .await
來等待另一個實作 future
trait 的型別完成任務,例如其他 async fn
的輸出。和 block_on
不同的事,.await
不會阻塞當前的執行緒,取而代之的是非同步地等待這個 future 完成,若這個 future 當下不能有所進展,也能允許其他任務繼續執行。
舉例來說,想像有三個 async fn
:learn_song
、sing_song
與 dance
:
# #![allow(unused_variables)] #fn main() { async fn learn_song() -> song { ... } async fn sing_song(song: song) { ... } async fn dance() { ... } #}
有個方法可以執行學習、唱歌和跳舞,就是分別阻塞每一個函式:
fn main() { let song = block_on(learn_song()); block_on(sing_song(song)); block_on(dance()); }
然而,這樣並沒有達到最佳的效能,我們同時只做了一件事而已!很明顯地,我們需要在唱歌之前學習歌曲,但能夠同時跳著舞有學習並唱歌。為了達成這個任務,我們建立兩個獨立可並行執行的 async fn
:
async fn learn_and_sing() { // Wait until the song has been learned before singing it. // We use `.await` here rather than `block_on` to prevent blocking the // thread, which makes it possible to `dance` at the same time. let song = learn_song().await; sing_song(song).await; } async fn async_main() { let f1 = learn_and_sing(); let f2 = dance(); // `join!` is like `.await` but can wait for multiple futures concurrently. // If we're temporarily blocked in the `learn_and_sing` future, the `dance` // future will take over the current thread. If `dance` becomes blocked, // `learn_and_sing` can take back over. If both futures are blocked, then // `async_main` is blocked and will yield to the executor. futures::join!(f1, f2); } fn main() { block_on(async_main()); }
這個範例中,學習歌曲必須發生在唱歌之前,但無論唱歌或學習都能與跳舞同時發生。如果我們在 learn_and_sing
中使用 block_on(learn_song())
而不是 learn_song().await
,整個執行緒在 learn_song
執行期間無法做其他事,這種情況下要同時跳舞是不可能的任務。利用 .await
來等待 learn_song
future,就會允許其他任務在 learn_song
阻塞時接管當前的執行緒。這讓在同個執行緒下並行執行多個future 變為可能。
現在,你學會了基礎的 async/await
,讓我們來嘗嘗實戰範例。