Tokio Select Timeout Cancellation

Bound async operations with tokio::select over a timeout future, racing work against deadlines and handling branch outcomes.

RustResilience

Code

snippet
use std::time::Duration;
use tokio::time::{sleep, timeout};

async fn fetch_quote(symbol: &'static str) -> String {
    sleep(Duration::from_millis(300)).await;
    format!("{symbol}=142.50")
}

async fn with_deadline(symbol: &'static str) -> Result<String, String> {
    let deadline = Duration::from_millis(150);

    // select 让慢操作与超时竞争,先完成者胜出
    tokio::select! {
        quote = fetch_quote(symbol) => Ok(quote),
        _ = sleep(deadline) => Err(format!("{symbol} exceeded {deadline:?}")),
    }
}

async fn stream_with_timeout() -> Vec<String> {
    let mut received = Vec::new();
    let (tx, mut rx) = tokio::sync::mpsc::channel(8);

    tokio::spawn(async move {
        for i in 0..5 {
            sleep(Duration::from_millis(40)).await;
            if tx.send(format!("tick-{i}")).await.is_err() {
                break; // 接收端已放弃
            }
        }
    });

    loop {
        let next = tokio::select! {
            msg = rx.recv() => msg,
            _ = sleep(Duration::from_millis(120)) => None,
        };
        match next {
            Some(msg) => received.push(msg),
            None => break, // 通道关闭或超时
        }
    }
    received
}

#[tokio::main]
async fn main() {
    println!("{:?}", with_deadline("APPL").await);

    let wrapped = timeout(Duration::from_millis(500), fetch_quote("MSFT")).await;
    println!("{:?}", wrapped.map(|s| s.len()));

    println!("{:?}", stream_with_timeout().await);
}