# Concurrency in JavaScript

## 1. Concurrency vs Parallelism — the Distinction That Matters Here

- **Parallelism**: multiple things physically executing at the exact same instant (requires multiple CPU cores/threads).
- **Concurrency**: multiple things **in progress** over overlapping time periods, but not necessarily executing at the same instant.

JavaScript's main thread is **single-threaded** — it cannot achieve true parallelism for regular JS code. What it achieves is **concurrency**: interleaving many in-progress async operations on one thread, switching between them at `await` points and callback boundaries, coordinated by the event loop (see the Event Loop topic).

![Concurrency interleaves A and B on one thread; parallelism runs A and B on two separate threads at the same instant](diagrams/01-concurrency-vs-parallelism.svg)

(A dedicated, deeper comparison of concurrency vs parallelism as general concepts is covered in the Advanced Engineering stack — this topic focuses specifically on how JS achieves concurrency without true parallel threads.)

## 2. How Single-Threaded Concurrency Actually Works

```js
async function taskA() {
  console.log("A: step 1");
  await delay(50);
  console.log("A: step 2");
}
async function taskB() {
  console.log("B: step 1");
  await delay(50);
  console.log("B: step 2");
}

taskA();
taskB();
// Output: A:1, B:1, (both waiting concurrently), A:2, B:2 (roughly together)
```

Neither task literally runs "at the same time" as the other — the engine runs `taskA`'s synchronous part, then `taskB`'s synchronous part, then both resume (via the microtask/macrotask queues) once their `delay` finishes. The *appearance* of simultaneity comes from interleaving, not literal parallel execution.

## 3. Where JavaScript DOES Get Real Parallelism

| Mechanism | Parallel? | Notes |
|---|---|---|
| `async`/`await`, Promises, callbacks | ❌ concurrency only | still one thread, just interleaved |
| **Web Workers** (browser) | ✅ true parallel threads | separate memory, communicate via `postMessage` |
| **Worker Threads** (Node.js) | ✅ true parallel threads | same idea, server-side (own topic in the Backend stack) |
| I/O itself (disk, network) | ✅ happens outside the JS thread entirely | handled by the OS/runtime in the background; JS just gets notified via a callback when done |

The reason async I/O (file reads, network requests) doesn't block the main thread isn't magic — the actual waiting happens in the OS or runtime layer, **outside** JavaScript's single thread; JS only re-engages once a result is ready.

## 4. Concurrency Enables Throughput, Not Raw Speed-Up of CPU Work

```js
// This does NOT get faster with concurrency — it's pure CPU work,
// blocking the single thread regardless of how "async" it looks:
async function heavyComputation() {
  let sum = 0;
  for (let i = 0; i < 1e9; i++) sum += i; // still fully blocks the thread
  return sum;
}
```

Concurrency helps massively with **I/O-bound** work (waiting on network/disk — the thread is free to do other things while waiting) but does **nothing** for **CPU-bound** work (the thread is genuinely busy computing, with nothing to interleave). CPU-bound work needs real parallelism — Worker Threads / Web Workers — not `async`/`await`.

## 5. A Practical Mental Model

```
I/O-bound task (network request, file read, timer)
  → mostly WAITING, not computing
  → concurrency (async/await) helps a lot — the thread is free during the wait

CPU-bound task (image processing, large sort, heavy math)
  → mostly COMPUTING, not waiting
  → concurrency does nothing — need real parallelism (Worker Threads)
```

## Under the Hood: What a Worker Actually Is

When section 3 says Web Workers / Worker Threads provide "true parallel threads," it's worth being precise about what's actually isolated and what isn't — because it explains both why workers achieve real parallelism and why they can't just share objects the way functions in the same thread do.

### Each worker gets its own Isolate — a full, separate engine instance

A Worker isn't just "the same JS running on another thread" — V8 spins up a completely separate **Isolate**: its own heap, its own garbage collector running independently, its own compiled-code caches, entirely isolated from the main thread's Isolate. This is a much stronger boundary than, say, two threads in a language like Java sharing one heap — two Isolates share **no memory** by default at all.

```
Main thread:  Isolate A — heap A, GC runs independently, Context chain A
Worker thread: Isolate B — heap B, GC runs independently, Context chain B
                (genuinely separate engine instances, not just separate call stacks)
```

### Why you can't just pass an object to a worker

```js
const worker = new Worker("worker.js");
worker.postMessage({ name: "Ana" }); // this object is NOT shared by reference
```

Because the two Isolates have entirely separate heaps, an object living in heap A has no meaning to code running against heap B — there's no shared address space to point into. `postMessage` doesn't hand over a reference; by default it performs a **structured clone**: a deep copy of the data, serialized out of one heap and reconstructed fresh inside the other. This is why functions, DOM nodes, and other non-cloneable values can't be sent via `postMessage` at all, and why sending large data structures repeatedly has a real copying cost — it's not free just because it "looks like" passing a reference.

### The one real exception: `SharedArrayBuffer`

For cases that genuinely need shared memory between threads, `SharedArrayBuffer` provides an actual shared region of raw memory both Isolates can read/write directly, without cloning — paired with `Atomics` for safe coordinated access (avoiding the classic race-condition problems true shared-memory concurrency introduces, a topic covered in the Advanced Engineering stack's Race Conditions topic). This is deliberately opt-in and rarely used compared to `postMessage`, precisely because safely sharing mutable memory across real parallel threads reintroduces the entire category of concurrency bugs (race conditions, torn reads) that JavaScript's single-threaded model normally avoids by construction.

## 6. Common Pitfalls

1. **Believing `async` makes code run on another thread** — it doesn't; `async`/`await` is entirely about *scheduling* on the same single thread, not multi-threading.
2. **Expecting concurrency to speed up CPU-heavy loops** — only real parallelism (Workers) helps here; wrapping a heavy loop in an `async function` changes nothing about its blocking behavior.
3. **Assuming "concurrent" means "simultaneous"** — in JS, concurrent tasks are interleaved on one thread, not literally running at the same instant (unless offloaded to Workers).
4. **Not offloading genuinely heavy computation** — a long synchronous loop inside an `async function` still freezes the event loop, blocking timers, network handling, and (in browsers) rendering, exactly as described in the Call Stack and Event Loop topics.

## 7. Quick Reference

```
JS main thread: single-threaded, concurrency via interleaving (event loop)
True parallelism: Web Workers / Worker Threads only
I/O-bound work → concurrency (async/await) helps a lot
CPU-bound work  → concurrency does nothing, need real threads
```
