Skip to Content
Course content

154: Spawning Tasks with tokio::spawn

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why can't I just .await my futures? When do I actually need spawn?

This is where a lot of people get tripped up. When you call .await on a future, you're telling the current task to pause until that specific future completes. It's sequential. If you have three API calls and you .await each one in order, you're just doing synchronous programming with extra steps. You aren't actually doing anything in parallel.

tokio::spawn is different. It takes a future and hands it over to the Tokio runtime to be executed independently. The runtime can then move that task to a different thread if it wants to. It's the difference between waiting for the toaster to finish before you start the coffee, and starting both at the same time.

use tokio::time::{sleep, Duration};

async fn fetch_data(id: u32) {
    println!("Starting fetch {}", id);
    sleep(Duration::from_secs(2)).await;
    println!("Finished fetch {}", id);
}

#[tokio::main]
async fn main() {
    // This is slow. It takes 4 seconds total.
    fetch_data(1).await;
    fetch_data(2).await;

    // This is fast. It takes 2 seconds total.
    tokio::spawn(fetch_data(1));
    tokio::spawn(fetch_data(2));
    
    // We sleep here just so the main function doesn't exit 
    // before the spawned tasks finish.
    sleep(Duration::from_secs(3)).await;
}

Why does the compiler keep complaining about 'static lifetimes?

I've spent way too many hours fighting this error. You'll likely see a message saying the future may outlive the current function, and that it requires a 'static lifetime.

Here's the deal: since tokio::spawn pushes the task onto the runtime's scheduler, Tokio has no idea when that task will actually finish. It might finish in a millisecond, or it might run for an hour. Because of that, the task cannot hold any references to data on the local stack—because that stack frame will be gone long before the task finishes. Everything the task uses must be owned by the task itself.

The fix is usually the move keyword. By putting move before the async block, you tell Rust to transfer ownership of the variables into the task.

use std::sync::Arc;

async fn process_config(config: Arc<String>) {
    println!("Processing: {}", config);
}

#[tokio::main]
async fn main() {
    let config = Arc::new("ServerAddr=127.0.0.1".to_string());

    // This would fail without 'move' because 'config' is a local variable
    tokio::spawn(async move {
        process_config(config.clone()).await;
    });
}

How do I get a value back from a spawned task?

When you call tokio::spawn, it doesn't just return "nothing." It returns a JoinHandle. Think of this as a receipt for the work you just ordered. You can .await that handle later to retrieve the return value of the async block.

One thing to watch out for: awaiting a JoinHandle returns a Result. This is because the task might have panicked. You have to handle that possibility, even if you're sure your code is safe.

#[tokio::main]
async fn main() {
    let handle = tokio::spawn(async {
        // Simulate some heavy calculation
        42
    });

    // Do other work here...

    match handle.await {
        Ok(value) => println!("The answer is {}", value),
        Err(e) => eprintln!("The task panicked: {:?}", e),
    }
}



📋 Practical Task

Exercise: Building a Concurrent Website Status Checker

Your goal is to create a program that checks the availability of multiple URLs concurrently. Instead of checking them one by one, you will spawn a separate Tokio task for each URL.

  • Create a list of at least three URLs (you can use "https://google.com", etc.).
  • Use a loop to iterate through the URLs.
  • Inside the loop, use tokio::spawn to start a task that simulates a network request (use tokio::time::sleep for 1-2 seconds to mimic latency).
  • Each spawned task should return a string: "URL [address] is UP".
  • Collect all the JoinHandles into a Vec.
  • After the loop, iterate through your Vec of handles and .await them to print the final status of every site.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.