Skip to content

Q067. When should you use join!, spawn, or select!?

Short Answer

Use join! when all futures should run concurrently and you need all results. Use select! when you need to react to whichever branch becomes ready first, often for timeout, shutdown, or racing operations. Use spawn when work needs an independent scheduling and ownership boundary. join! and select! poll branches concurrently in one task; they do not make CPU work parallel.

Deep Dive

The choice expresses lifecycle:

  • join! keeps all child futures structurally owned by the parent and completes after all do;
  • select! completes one enabled branch and usually drops or reuses the losing futures according to surrounding control flow;
  • spawn creates independently scheduled tasks that require explicit joining, cancellation, and error handling.

For fallible futures, Tokio also provides try_join!, which returns on the first error. select! requires every losing operation to be cancellation-safe if dropping and recreating it could lose progress.

Spawning may provide scheduling isolation and parallel execution on a multi-thread runtime, but it adds allocation, scheduler, and 'static/Send constraints. Do not use it solely to satisfy the borrow checker.

Internal Model

join! and select! store child futures in the current task and poll them from the parent's poll. spawn allocates task metadata and adds the task to runtime queues.

Tokio rotates polling order in join! by default for fairness. select! uses randomized branch-check order unless biased; is specified.

Example

rust
async fn value(number: u64) -> u64 {
    tokio::task::yield_now().await;
    number
}

#[tokio::main]
async fn main() {
    let (left, right) = tokio::join!(value(20), value(22));
    assert_eq!(left + right, 42);
}

Common Mistakes

  • Using spawn when structured join! is sufficient.
  • Saying join! runs each branch on a separate thread.
  • Racing a cancellation-unsafe operation in select!.

Follow-ups

  • How does try_join! handle already-started sibling futures?
  • When does spawning improve fairness or isolation?
  • What does biased; change in select!?

References

  • Tokio Documentation
  • Async Book