Differences between Thread.sleep and Task.sleep explained
Published on: May 1, 2025In Swift, we have several ways to “suspend” execution of our code. While that’s almost always a bad practice, I’d like to explain why Task.sleep
really isn’t as problematic as you might expect when you’re familiar with Thread.sleep
.
When you look for examples of debouncing or implementing task timeout they will frequently use Task.sleep
to suspend a task for a given amount of time.
The key difference is in how tasks and threads work in Swift.
In Swift concurrency, we often say that tasks replace threads. Or in other words, instead of worrying about threads, we worry about tasks.
While that not untrue, it’s also a little bit misleading. It sounds like tasks and threads are mostly analogous to each other and thats not the case.
A more accurate mental model is that without Swift concurrency you used Dispatch Queues to schedule work on threads. In Swift concurrency, you use tasks to schedule work on threads. In both cases, you don’t directly worry about thread management or creation.
Exploring Thread.sleep
When you suspend execution of a thread using Thread.sleep
you prevent that thread from doing anything other than sleeping. It’s not working on dispatch queues, nor on tasks.
With GCD that’s bad but not hugely problematic because if there are no threads available to work on our queue, GCD will just spin up a new thread.
Swift Concurrency isn’t as eager to spin up threads; we only have a limited number of threads available.
This means that if you have 4 threads available to your program, Swift Concurrency can use those threads to run dozens of tasks efficiently. Sleeping one of these threads with Thread.sleep
means that you now only have 3 threads available to run the same dozen of tasks.
If you hit a Thread.sleep
in four tasks, that means you’re now sleeping every thread available to your program and your app will essentially stop performing any work at all until the threads resume.
What about Task.sleep?
Sleeping a task with Task.sleep
is, in some ways, quite similar to Thread.sleep
. You suspend execution of your task, preventing that task to make progress. The key difference in how that suspension happens. Sleeping a thread just stops it from working and reducing the number of threads available. Sleeping a task means you suspend the task, which allows the thread that was running your task to start running another task.
You’re not starving the system from resources with Task.sleep
and you’re not preventing your code from making forward progress which is absolutely essential when you’re using Swift Concurrency.
If you find yourself needing to suspend execution in your Swift Concurrency app you should never use Thread.sleep
and use Task.sleep
instead. I don’t say never often, but this is one of those cases.
Also, when you find yourself adding a Task.sleep
you should also make sure that you’re using it to solve a real problem and not just because “without sleeping for 0.01 seconds this didn’t work properly”. Those kinds of sleeps usually mask serialization and queueing issues that should be solved instead of hidden.