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.

count
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(), |_| {}, |_| {}, |_| {});
  1. 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}");
  2. 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 {
  3. 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}")
  4. 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}
  5. 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
  1. 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}");
  2. 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 {
  3. 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}")
  4. 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}
  5. 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
  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}");
  2. 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 {
  3. 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}")
  4. 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}
  5. 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.