Пример async
/.await
async
/.await
- это встроенные в Rust
инструменты для написания асинхронного кода.
async
преобразует блок кода в конечный автомат,
который реализует типаж, зовущийся Future
. В то
время как вызов блокирующей функции в синхронном методе
заблокирует весь поток, блокировка Future
вернёт
контроль над потоком, позволяя работать другим
Future
.
Для создания асинхронной функции, вы можете использовать
синтаксис async fn
:
# #![allow(unused_variables)] #fn main() { async fn do_something() { ... } #}
Значение, возвращённоеasync fn
- Future
. Что бы ни произошло, Future
должна быть запущена в исполнителе.
// `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
(например,
полученного из другой async fn
). В отличие от
block_on
, .await
не блокирует
текущий поток, но асинхронно ждёт завершения футуры, позволяя
другим задачам выполняться, если в данный момент футура не
может добиться прогресса.
Например, представим что у нас есть три 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()); }
В этом примере, запоминание песни должно быть сделано до
пения песни, но и запоминание и пение могут завершиться
одновременно с танцем. Если мы используем
block_on(learn_song())
вместо
learn_song().await
в learn_and_sing
,
поток не может делать ничего другого, пока запущена
learn_song
. Из-за этого мы одновременно с этим не
можем танцевать. Пока ожидается (.await
) футура
learn_song
, мы разрешаем другим задачам
захватить текущий поток, если learn_song
заблокирована. Это делаем возможным запуск нескольких футур,
завершающихся параллельно в одном потоке.
Теперь мы изучили основы async
/await
, давайте посмотрим их в работе.