Skip to content

Concurrency Overview

JavaScript runs on a single thread. The event loop processes one task at a time and delegates I/O to the operating system. Concurrency in JS is cooperative: your code yields control by hitting an await, and the event loop picks up the next queued callback. Nothing truly runs in parallel inside your JS process unless you spin up a Web Worker — a separate process that communicates only via postMessage.

Rust takes a radically different approach. You get real OS threads out of the box, a first-class async/await system that works on top of a user-space runtime (Tokio is the standard choice), and a compiler that statically prevents data races at compile time. Rust calls this fearless concurrency — you can write concurrent code and the compiler will refuse to compile programs that have data-race bugs.

ModelRust primitiveJS analogy
OS threads (parallel CPU work)std::thread::spawnWeb Workers (limited)
Async I/O (many concurrent tasks, one thread pool)async/await + TokioPromise / async/await
Shared mutable state (threads)Arc<Mutex<T>>SharedArrayBuffer + Atomics
Message passing (threads)std::sync::mpscworker.postMessage

The Rust ownership and type system enforces two rules at compile time:

  • A value can have many readers or one writer — never both simultaneously.
  • Data shared across threads must implement the Send and Sync marker traits, which the compiler verifies automatically.

If you try to share a raw &mut T across threads, the program will not compile. No runtime lock, no silent corruption — the bug is caught before your code runs.

TypeScript
// JavaScript: single-threaded, concurrency via event loop
async function fetchAll(urls: string[]): Promise<string[]> {
// All fetches start "in parallel" (concurrent I/O)
// but JS code itself runs on one thread
return Promise.all(urls.map(url => fetch(url).then(r => r.text())));
}
// Web Worker for CPU-bound parallelism (separate process)
const worker = new Worker('./worker.js');
worker.postMessage({ data: [1, 2, 3] });
worker.onmessage = (e) => console.log(e.data);
Rust
use std::thread;
fn main() {
// Real OS thread — runs truly in parallel on another CPU core
let h1 = thread::spawn(|| "fearless");
let h2 = thread::spawn(|| "concurrency");
// join() waits for the thread and returns its result
let r1 = h1.join().unwrap();
let r2 = h2.join().unwrap();
println!("{r1} {r2}"); // fearless concurrency
}
use std::thread;
fn main() {
let h1 = thread::spawn(|| "fearless");
let h2 = thread::spawn(|| "concurrency");
let r1 = h1.join().unwrap();
let r2 = h2.join().unwrap();
println!("{r1} {r2}");
}
How does JavaScript achieve concurrency?
What does Rust's 'fearless concurrency' mean?
Which Rust primitive is most analogous to JS's async/await + Promise.all?