Limit Async Concurrency with a Semaphore

Share a tokio semaphore across spawned tasks, hold a permit for each job, and collect every result with JoinSet.

RustResilience

Code

snippet
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::Semaphore;
use tokio::task::JoinSet;
use tokio::time::sleep;

#[tokio::main]
async fn main() {
    let limit = Arc::new(Semaphore::new(2));
    let mut tasks = JoinSet::new();

    for id in 0..6 {
        let permits = Arc::clone(&limit);
        tasks.spawn(async move {
            let _permit = permits.acquire_owned().await.expect("semaphore closed");
            sleep(Duration::from_millis(30)).await;
            id * id
        });
    }

    let mut results = Vec::new();
    while let Some(joined) = tasks.join_next().await {
        results.push(joined.expect("task panicked"));
    }
    results.sort_unstable();
    println!("{results:?}");
}