Skip to content

04 — Sync vs Async

rebeloper edited this page Jul 12, 2026 · 2 revisions

🍽️ Intuition

Not every customer waits for their order the same way.

Some customers stand at the counter — they placed the order, and they're not moving until it's in their hands. They can't do anything else in the meantime; they're just standing there.

Other customers get a buzzer 🔔. They place the order, walk off to find a table, check their phone, chat with a friend — and only come back when the buzzer goes off.

  • Stand at the counter, do nothing until the order arrives → sync
  • Take the buzzer, walk away, come back when it goes off → async

👉 Both customers get their order. The difference is whether they're free to do anything else while it's being made.


🧠 What It Means In Swift

DispatchQueue gives you two ways to submit a block, and they behave completely differently:

  • .sync — submits the block and blocks the calling thread until that block finishes. Whatever called .sync cannot move on to its next line of code until the work is done.
  • .async — submits the block and returns immediately. The calling thread keeps going with its next line of code right away; the submitted block runs later, whenever the queue gets around to it.
queue.sync { ... }   // caller waits here, standing at the counter
queue.async { ... }  // caller gets a buzzer and walks away

👉 .sync and .async don't change what work gets done — they change whether the caller waits for it.

One thing to be precise about: this is separate from serial vs. concurrent from Chapter 3. Serial vs. concurrent is about the queue — how many blocks it lets run at once. Sync vs. async is about the caller — whether it waits around for the block to finish. You can .sync onto a concurrent queue, and you can .async onto a serial one.


📊 ASCII Diagram

SYNC — caller waits at the counter 🧍

Caller: ──[submit]──[.......waiting.......]──[continues]──>
Queue:            └──> 🧑‍🍳 cooks the order ──┘


ASYNC — caller takes the buzzer and walks away 🔔

Caller: ──[submit]──[continues immediately]──────────────>
Queue:            └──> 🧑‍🍳 cooks the order ──> 🔔 (done, whenever)

💻 Tiny Swift Example

queue.sync {
    print("This finishes before the next line runs")
}
print("Runs after the sync block")

queue.async {
    print("This may run before or after the next line")
}
print("Runs immediately, doesn't wait for async block")

👉 The .sync block is guaranteed to print before "Runs after the sync block." The .async block might print before or after "Runs immediately" — you genuinely don't know, and you don't need to.


🔄 vs. Swift Concurrency

await in Swift Concurrency looks like .sync — the code underneath it reads top-to-bottom, in order, as if it's waiting right there. But it behaves like neither .sync nor .async on its own:

  • Like .sync, the code after await doesn't run until the awaited work finishes — you get sequential, readable order.
  • Unlike .sync, await doesn't block the thread while it waits. It suspends the current task, freeing the thread to go do other work, and resumes later — possibly on a different thread — when the result is ready.

👉 Think of await as a customer with a buzzer who chooses to stand right next to the pickup counter instead of wandering off — they still get notified the instant it's ready, but the thread underneath them isn't frozen; it can go serve someone else while they "wait."


🧸 Memory Sentence

Sync waits at the counter; async gets a buzzer.


⚠️ Analogy Limit

A real buzzer is harmless — worst case, your table gets cold food a minute later. Nobody else in the restaurant is affected by you standing at the counter, either.

.sync is not so forgiving:

  • If you call .sync on a queue, and that queue is already busy running the exact serial queue you're calling from, you and the queue end up waiting on each other forever — a deadlock. Chapter 8 covers this in full.
  • A misused .sync doesn't just make one customer wait a little longer — it can freeze the queue, and everything behind it, indefinitely.

So "standing at the counter" captures the waiting, but not the fact that a badly placed .sync call can lock up your entire app, not just one customer's afternoon.


✅ Check Your Understanding

When would you actually choose .sync over .async? What are you giving up — and what are you gaining — by making the caller wait?

Clone this wiki locally