Swift
Concurrency
Non-blocking
Sleep
Programming

Swift Concurrency - non-blocking sleep?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Swift concurrency, the non-blocking way to pause work is Task.sleep. It suspends the current async task without blocking the underlying thread, which is exactly what you want in structured concurrency. Thread.sleep, by contrast, blocks a real thread and should not be your default in async code.

What Non-Blocking Sleep Means

A blocking sleep occupies the thread for the whole delay. A non-blocking sleep suspends the task and lets the executor run other work.

That distinction matters because async functions are designed to cooperate with the runtime scheduler. If you block the thread, you reduce concurrency and can make the app feel less responsive.

Use Task.sleep in Async Code

Modern Swift gives you this pattern:

swift
1import Foundation
2
3func demo() async {
4    print("before")
5    try? await Task.sleep(for: .seconds(1))
6    print("after")
7}
8
9Task {
10    await demo()
11}
12
13RunLoop.main.run()

Older forms use nanoseconds directly:

swift
try await Task.sleep(nanoseconds: 1_000_000_000)

The duration-based form is usually easier to read.

Cancellation Matters

Task.sleep is cancellable. If the task is canceled while sleeping, the call throws. That is a feature, not a nuisance, because sleeping work should usually stop if the task itself is no longer needed.

swift
1func delayedMessage() async {
2    do {
3        try await Task.sleep(for: .seconds(5))
4        print("done")
5    } catch {
6        print("sleep canceled")
7    }
8}

If cancellation should propagate, do not ignore the error silently.

When Thread.sleep Is Still Wrong

You may still see code like this:

swift
Thread.sleep(forTimeInterval: 1.0)

That is fine only in old synchronous code where blocking is intentional and harmless. Inside async workflows, it defeats the point of Swift concurrency.

Use Task.sleep in:

  • retry backoff logic
  • debouncing async tasks
  • test helpers in async code
  • temporary pacing of background work

A Practical Retry Example

Non-blocking sleep is especially useful in retry logic.

swift
1func fetchWithRetry() async {
2    for attempt in 1...3 {
3        do {
4            print("attempt", attempt)
5            if attempt < 3 {
6                throw URLError(.timedOut)
7            }
8            print("success")
9            return
10        } catch {
11            if attempt == 3 { return }
12            try? await Task.sleep(for: .milliseconds(500))
13        }
14    }
15}

This pauses between attempts without tying up a thread. The same principle applies to small orchestration delays, but if your code is waiting for a real event, an async callback or sequence is usually better than sleeping and polling.

Another practical point is testability. A sleep-based delay can be acceptable in small examples, but production workflows often benefit from injecting a clock or delay abstraction so long waits are not hard-coded into tests. That keeps async code fast to test and easier to reason about. This matters most in retry-heavy code, where several real waits can make the test suite unnecessarily slow. It is a small design choice, but it keeps concurrency code maintainable as the workflow grows.

Common Pitfalls

The biggest mistake is using Thread.sleep inside async code and assuming it is equivalent. It is not.

Another mistake is forgetting that Task.sleep can throw when the task is canceled. If cancellation matters, handle that path intentionally.

A third mistake is using sleep where a real synchronization primitive, timer, or event-driven callback would be better. Sleep is for delay, not for correctness.

Summary

  • In Swift concurrency, use Task.sleep for non-blocking delays.
  • 'Task.sleep suspends the task instead of blocking the underlying thread.'
  • Prefer the duration-based form when available because it is easier to read.
  • Expect cancellation and handle the thrown error appropriately.
  • Avoid Thread.sleep in async code unless you intentionally want to block a real thread.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.