Difference between DispatchQueue.main.async and DispatchQueue.main.sync
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
DispatchQueue.main.async and DispatchQueue.main.sync both send work to the main queue, but they do it with very different blocking behavior. async schedules the work and returns immediately, while sync blocks the current thread until the work finishes. That difference is crucial on iOS because the main queue is also the UI queue, and blocking it incorrectly can freeze the app or deadlock it.
What main.async Does
DispatchQueue.main.async places a block on the main queue and lets the caller continue immediately.
This is the normal pattern when background work finishes and you need to update the UI safely.
Key behavior:
- work is queued on the main queue
- the calling thread does not wait
- the block runs later when the main queue is ready
What main.sync Does
DispatchQueue.main.sync also submits work to the main queue, but the caller waits until that work finishes.
Here the background thread blocks until the main queue executes the block.
That can be valid in rare cases, but it is much more dangerous because it introduces waiting between threads.
Why main.sync Can Deadlock
If you call DispatchQueue.main.sync from code that is already running on the main queue, the app deadlocks.
The main queue is waiting for itself to finish a block that it cannot start because it is already busy executing the current block. That circular wait is a deadlock.
This is why main.sync should be treated with caution and is almost never the right choice for UI code.
When To Use Each One
Use DispatchQueue.main.async when:
- you are updating UI from a background queue
- you want to schedule work on the main queue without blocking the caller
- you want to avoid deadlock risk in common app code
Use DispatchQueue.main.sync only when:
- you are definitely not already on the main queue
- you deliberately need the caller to wait for the main-queue work to finish
- you have a strong reason and understand the threading consequences
In ordinary app development, main.async is the correct answer far more often.
A Practical UI Example
This keeps the UI responsive and updates the label on the correct queue.
If you replaced main.async with main.sync here, the background thread would wait for the main thread unnecessarily. That may still work, but it provides no benefit and increases coupling.
Common Pitfalls
The most common mistake is calling DispatchQueue.main.sync from code that already runs on the main thread. That deadlocks immediately.
Another issue is using sync when async would do. If you only need to schedule a UI update, blocking the current thread is wasted work.
It is also easy to forget that heavy work should not run on the main queue at all. async is not magic if the block itself is expensive.
Finally, do not assume sync is "more correct" because it is more deterministic. It is only appropriate when you intentionally need blocking semantics.
Summary
- Both methods dispatch work to the main queue, but
asyncdoes not block andsyncdoes. - '
DispatchQueue.main.asyncis the normal way to update UI from background work.' - '
DispatchQueue.main.synccan deadlock if called from the main queue.' - Use
synconly when you intentionally need the caller to wait and know you are off the main queue. - In typical app code, prefer
main.asyncfor safety and responsiveness.

