Async Concepts
Await Chain
One Future Uses Another
Inside an async function, .await waits for another future and then continues with its returned value.
Program
Play the program to choose a count and watch one async function await another before formatting the label.
await_chain.rs
Replay: real traced execution (multi-file project)
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
fn main() {
let count = 2;
let future = build_label("task", count);
let label = block_on(future);
println!("{label}");
}
async fn build_label(prefix: &str, count: i32) -> String {
let label = make_label(prefix).await;
format!("{label}:{count}")
}
async fn make_label(prefix: &str) -> String {
format!("{prefix}-ready")
}
fn block_on<F: Future>(future: F) -> F::Output {
let waker = noop_waker();
let mut context = Context::from_waker(&waker);
let mut future = pin!(future);
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => return value,
Poll::Pending => {}
}
}
}
fn noop_waker() -> Waker {
unsafe { Waker::from_raw(noop_raw_waker()) }
}
fn noop_raw_waker() -> RawWaker {
RawWaker::new(std::ptr::null(), &VTABLE)
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
fn main() {
let count = 1;
let future = build_label("task", count);
let label = block_on(future);
println!("{label}");
}
async fn build_label(prefix: &str, count: i32) -> String {
let label = make_label(prefix).await;
format!("{label}:{count}")
}
async fn make_label(prefix: &str) -> String {
format!("{prefix}-ready")
}
fn block_on<F: Future>(future: F) -> F::Output {
let waker = noop_waker();
let mut context = Context::from_waker(&waker);
let mut future = pin!(future);
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => return value,
Poll::Pending => {}
}
}
}
fn noop_waker() -> Waker {
unsafe { Waker::from_raw(noop_raw_waker()) }
}
fn noop_raw_waker() -> RawWaker {
RawWaker::new(std::ptr::null(), &VTABLE)
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
fn main() {
let count = 3;
let future = build_label("task", count);
let label = block_on(future);
println!("{label}");
}
async fn build_label(prefix: &str, count: i32) -> String {
let label = make_label(prefix).await;
format!("{label}:{count}")
}
async fn make_label(prefix: &str) -> String {
format!("{prefix}-ready")
}
fn block_on<F: Future>(future: F) -> F::Output {
let waker = noop_waker();
let mut context = Context::from_waker(&waker);
let mut future = pin!(future);
loop {
match future.as_mut().poll(&mut context) {
Poll::Ready(value) => return value,
Poll::Pending => {}
}
}
}
fn noop_waker() -> Waker {
unsafe { Waker::from_raw(noop_raw_waker()) }
}
fn noop_raw_waker() -> RawWaker {
RawWaker::new(std::ptr::null(), &VTABLE)
}
static VTABLE: RawWakerVTable = RawWakerVTable::new(|_| noop_raw_waker(), |_| {}, |_| {}, |_| {});
count ← 2, future ← (empty)
5fn main() {6 let coun→ 2t = 2; //@count=2, 1, 37 let futur→ (empty)e = build_label("task", coun2t);8 let label = block_on(futur(empty)e);9 println!("{label}");waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }
21fn block_on<F: Future>(future: F) -> F::Output {22 let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();23 let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);24 let mut futur→ (empty)e = pin!(future);25 loop {fn build_label(prefix: &str, count: i32) -> String
12async fn build_label(prefix: &str, count: i32) -> String {13 let label = make_label(prefi"task"x).await;14 format!("{label}:{count}")label ← "task-ready"
12async fn build_label(prefix: &str, count: i32) -> String {13 let labe→ "task-ready"l = make_label(prefi"task"x).await;14 format!("{label}:{count}")15}label ← "task-ready:2"
7 let future = build_label("task", count);8 let labe→ "task-ready:2"l = block_on(futur(empty)e);9 println!("{label}");10}outputtask-ready:2
count ← 1, future ← (empty)
5fn main() {6 let coun→ 1t = 1;7 let futur→ (empty)e = build_label("task", coun1t);8 let label = block_on(futur(empty)e);9 println!("{label}");waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }
21fn block_on<F: Future>(future: F) -> F::Output {22 let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();23 let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);24 let mut futur→ (empty)e = pin!(future);25 loop {fn build_label(prefix: &str, count: i32) -> String
12async fn build_label(prefix: &str, count: i32) -> String {13 let label = make_label(prefi"task"x).await;14 format!("{label}:{count}")label ← "task-ready"
12async fn build_label(prefix: &str, count: i32) -> String {13 let labe→ "task-ready"l = make_label(prefi"task"x).await;14 format!("{label}:{count}")15}label ← "task-ready:1"
7 let future = build_label("task", count);8 let labe→ "task-ready:1"l = block_on(futur(empty)e);9 println!("{label}");10}outputtask-ready:1
count ← 3, future ← (empty)
5fn main() {6 let coun→ 3t = 3;7 let futur→ (empty)e = build_label("task", coun3t);8 let label = block_on(futur(empty)e);9 println!("{label}");waker ← Waker { data: 0x0, vtable: ⟨addr A⟩ }, context ← Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }
21fn block_on<F: Future>(future: F) -> F::Output {22 let wake→ Waker { data: 0x0, vtable: ⟨addr A⟩ }r = noop_waker();23 let mut contex→ Context { waker: Waker { data: 0x0, vtable: ⟨addr A⟩ } }t = Context::from_waker(&wakeWaker { data: 0x0, vtable: ⟨addr A⟩ }r);24 let mut futur→ (empty)e = pin!(future);25 loop {fn build_label(prefix: &str, count: i32) -> String
12async fn build_label(prefix: &str, count: i32) -> String {13 let label = make_label(prefi"task"x).await;14 format!("{label}:{count}")label ← "task-ready"
12async fn build_label(prefix: &str, count: i32) -> String {13 let labe→ "task-ready"l = make_label(prefi"task"x).await;14 format!("{label}:{count}")15}label ← "task-ready:3"
7 let future = build_label("task", count);8 let labe→ "task-ready:3"l = block_on(futur(empty)e);9 println!("{label}");10}outputtask-ready:3
.await
`.await` pauses the surrounding async function until the awaited future is ready.
continuation
After `make_label(prefix).await`, `build_label` continues with the returned string.
composition
Async functions can be composed by awaiting one future inside another.