Last Updated on November 14, 2023
Asyncio tasks will run until they choose to suspend and yield the control of execution.
This can be a problem in some tasks that call regular Python functions and methods, a they may not allow other tasks in the asyncio event loop to run.
Asyncio provides the ability for tasks to explicitly yield control on demand by sleeping via asyncio.sleep().
In this tutorial, you will discover how to sleep tasks in asyncio.
After completing this tutorial, you will know:
- How to sleep tasks in asyncio programs.
- The difference between asyncio.sleep() coroutine and the regular time.sleep() function.
- How we can yield control within a task without sleeping for any time.
Let’s get started.
What is asyncio sleep()
Sleep is an important part of asyncio programs and is provided by the sleep() function.
The asyncio.sleep() function provides a non-blocking sleep operation.
Sleep means that the current execution is paused or suspended (blocked) for a fixed time.
It is referred to as a “non-blocking“, which means it does not block the current thread from executing. This means while one coroutine or task is sleeping, other tasks and coroutines can execute.
sleep() always suspends the current task, allowing other tasks to run.
— Coroutines and Tasks
Now that we know what the sleep() is, let’s look at how to use it.
Run loops using all CPUs, download your FREE book to learn how.
How to Use Asyncio sleep()
The asyncio.sleep() is a coroutine.
Technically, it is not a function but instead creates and returns a coroutine object. Nevertheless, it is easy to refer to it as the sleep() function or the sleep() module function.
The sleep() coroutine takes two arguments.
The first argument is “delay” and defines the time in seconds that the current task or coroutine will block. It is a positional argument and does not need to be specified by name.
For example:
1 2 3 |
... # sleep for 1 second await asyncio.sleep(1) |
The second argument is “result” which will return the provided value after a given the sleep has been completed.
For example:
1 2 3 |
... # sleep for one second and return 100 value = await asyncio.sleep(1, result=100) |
Returning a value can be helpful within an expression, providing a way to delay the release of a value.
For example:
1 2 3 4 |
... # delay a conditional expression if value > asyncio.sleep(5, 100): # ... |
The asyncio.sleep() function itself does not block, but instead, it returns an awaitable object that can be awaited.
Specifically, the asyncio.sleep() function returns a coroutine.
This coroutine can be stored in a variable and awaited at a later time.
For example:
1 2 3 4 5 6 |
... # get the sleep coroutine coro = asyncio.sleep(1) ... # block by awaiting the sleep coroutine await coro |
Now that we know how to use the asyncio.sleep() function, let’s look at some further examples of how we might sleep for different time intervals.
Asyncio sleep() for Milliseconds
The asyncio.sleep() function can be used to sleep the current task or coroutine for a given number of milliseconds.
Recall that one second is 1,000 milliseconds.
We can specify the number of milliseconds to sleep as a fraction of a second.
For example, 0.100 seconds equals 100 milliseconds or 1/10th of a second.
1 2 3 |
... # sleep for 100 milliseconds await asyncio.sleep(0.1) |
Asyncio sleep() for Seconds
The asyncio.sleep() function takes a time to block in seconds, as we have seen.
We can sleep for a given number of seconds, such as 1, 10, or 60.
For example:
1 2 3 |
... # block for 10 seconds await asyncio.sleep(10) |
We can also sleep for zero seconds.
This is a special sleep period. It means that the current task or coroutine will be suspended and the asyncio event loop will allow other tasks or coroutines to execute.
The current coroutine or task will then resume as soon as it is able, which may be more than 0 seconds.
It is a way that one task can give up control for a moment to allow other tasks to execute.
Asyncio sleep() for Minutes
The asyncio.sleep() function can be used to block the current task for minutes.
This may be helpful for a long-running task or a task that is required to perform a task periodically, such as every minute or every hour.
This can be achieved by determining the number of minutes to sleep and multiplying the value by 60 to convert it to seconds, before providing it to the sleep() function as an argument.
For example:
1 2 3 |
... # sleep for 15 minutes await asyncio.sleep(15 * 60) |
Now that we know how to use the asyncio.sleep() function, let’s consider when we might use it.
When to Use Asyncio sleep()
The asyncio.sleep() function has many uses.
In practice, it can be used to allow a task to wait a fixed time before performing an action.
This can be helpful in order to avoid overloading a resource, such as a remote server or database.
For example:
1 2 3 |
... # wait a moment before querying the database again await asyncio.sleep(0.5) |
A sleep can also be used to wait for a condition in a program, such as for another task to complete or for a variable to be set to True.
This is called a busy wait and is performed in a loop and allows the waiting task to perform actions while also waiting for a condition.
For example:
1 2 3 4 5 6 7 8 9 |
... # run forever while True: # check condition if condition(): # stop waiting break # otherwise sleep for a moment await asyncio.sleep(0.2) |
You can learn more about busy wait loops in the tutorial:
Another important use for sleep in asyncio programs is to suspend the current task and allow other coroutines to execute.
It is important because although a task or coroutine can easily schedule new tasks via the create_task() or gather() function, the scheduled tasks will not begin executing until the current task is suspended.
Even sleeping for zero seconds is enough to suspend the current task and give an opportunity to other tasks to run.
For example:
1 2 3 |
... # allow other tasks to run for a moment await asyncio.sleep(0) |
Finally, a good use for sleep is to simulate blocking tasks in a concurrent program.
Using calls to asyncio.sleep() in place of real file or socket IO allows us to develop, test, configure, and refine concurrent real-world programs without the need to use resources.
Now that we know when to use the sleep() function, let’s look at how it compares to other sleep functions.
Free Python Asyncio Course
Download your FREE Asyncio PDF cheat sheet and get BONUS access to my free 7-day crash course on the Asyncio API.
Discover how to use the Python asyncio module including how to define, create, and run new coroutines and how to use non-blocking I/O.
asyncio.sleep() vs time.sleep()
Another sleep function in Python is provided by the time module called time.sleep().
For example:
1 2 3 |
... # sleep for one seconds time.sleep(1) |
Like asyncio.sleep(), the time.sleep() takes a time in seconds and blocks the current unit of computation.
The important difference between asyncio.sleep() and time.sleep() is that asyncio.sleep() is non-blocking and time.sleep() is blocking.
Let me explain.
- time.sleep(): blocks the current thread. While blocked, other threads may run.
- asyncio.sleep(): blocks the current coroutine (asyncio task). While blocked, other coroutines may run.
The difference is the unit of concurrency that is blocked: threads vs coroutines (asyncio tasks).
Recall that one thread may run many coroutines, in fact, many thousands of coroutines. In turn, a process may run many threads, often many thousands of threads.
- One system runs many processes (multiprocessing concurrency).
- One process runs many threads (threading concurrency).
- One thread runs many coroutines (asyncio concurrency).
Blocking the thread will block all coroutines.
We do not want to block the thread in an asyncio program, it will halt all coroutine-based concurrency, which would be undesirable.
Therefore we do not want to call time.sleep() within an asyncio program.
We do want to call time.sleep() within a multi-threaded program to allow other threads in the program to run.
To learn more about how to use time.sleep(), see the tutorial:
Another important difference is that calling time.sleep() blocks immediately. It is called a sleep routine in the C code and ultimately a sleep routine in the underlying operating system.
The asyncio.sleep() function does not block. In fact, it returns immediately. The value it returns is a coroutine object that a program can choose to await or not.
- time.sleep(): Blocks the current thread, returns nothing.
- asyncio.sleep(): Returns a coroutine object that can be awaited.
Now that we know all about the asyncio.sleep() function, let’s look at some worked examples.
Overwhelmed by the python concurrency APIs?
Find relief, download my FREE Python Concurrency Mind Maps
Example of asyncio.sleep() in a Coroutine
We can explore using the sleep function within a coroutine.
In this example, we will define a new custom coroutine. It will first report a message, then block by sleeping for one second. After it resumes, it will report a message before terminating.
This new coroutine is created and awaited from the main coroutine.
The complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# SuperFastPython.com # example of sleeping a coroutine import asyncio # custom coroutine async def custom_coro(): # report a message print('coroutine running') # block for a moment await asyncio.sleep(1) # report a message print('coroutine done') # entry point coroutine async def main(): # execute another coroutine await custom_coro() # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then creates our custom coroutine and suspends, waiting for it to complete.
The custom coroutine runs, first reporting a message. It then blocks, sleeping for one second.
Other coroutines could be running at this time.
The main coroutine does not resume because it is awaiting the custom coroutine. The custom coroutine does not resume because it is awaiting the sleep coroutine.
The sleep ends and the custom coroutine resumes. A second message is reported and the coroutine terminates.
This highlights how we can sleep within an asyncio coroutine.
1 2 |
coroutine running coroutine done |
Next, let’s look at what we might call sleep from within a task.
Example of asyncio.sleep() in a Task
We can call sleep from a Task.
Recall that an asyncio task is just a coroutine that is wrapped in an asyncio.Task object so that it can be scheduled and executed independently within the asyncio event loop.
You can learn more about tasks in the tutorial:
The example below creates a task that reports a message, sleeps for a moment then reports a final message before terminating.
The task is created in the main coroutine which then waits for it to complete.
The complete example is listed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# SuperFastPython.com # example of sleeping a task import asyncio # custom coroutine async def custom_coro(): # report a message print('task running') # block for a moment await asyncio.sleep(1) # report a message print('task done') # entry point coroutine async def main(): # execute another task await asyncio.create_task(custom_coro()) # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then creates our custom coroutine and uses it to create a task and suspends, waiting for the task completed.
The task runs. It reports a message, sleeps for one second, then reports a final message.
This highlights how we can sleep within an asyncio task, just like we do in a coroutine.
1 2 |
task running task done |
Next, let’s look at the return value from the sleep() function.
Example of asyncio.sleep() Return Value
The asyncio.sleep() function returns a value.
Although, this may not be clear from the usage of the sleep() function in most programs.
Specifically, the sleep() function returns immediately with a coroutine object. This object can then be awaited.
Before awaiting it, we can inspect it, such as reporting its type and details.
This is a good practice in order to better understand what exactly the sleep function is doing.
The example below explores the coroutine returned from the sleep() function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# SuperFastPython.com # example of the return value from sleep import asyncio # entry point coroutine async def main(): # get the sleep awaitable awaitable = asyncio.sleep(0.1) # report the awaitable print(type(awaitable)) print(awaitable) # await the awaitable await awaitable # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then calls the sleep function which does not block, but instead returns immediately with an awaitable.
We then report the type of the returned awaitable. We can see that it is a coroutine.
When they report the details of the object and can see that it is a coroutine created from the sleep() coroutine definition, as we expected.
Finally, we await the sleep coroutine, suspending the main coroutine for a moment and allowing the sleep coroutine to run, which does nothing other than block, but does allow other coroutines to run, if they were present.
The sleep coroutine returns, then our main() coroutine terminates.
This example highlights the non-blocking coroutine nature of the sleep function.
1 2 |
<class 'coroutine'> <coroutine object sleep at 0x102d44dd0> |
Next, let’s explore the case of sleeping for zero seconds.
Example of asyncio.sleep(0) to Let Another Task Run
A common asyncio idiom is to sleep for zero seconds.
For example:
1 2 3 |
... # sleep for zero seconds asyncio.sleep(0) |
This idiom is common because it suspends the current coroutine and allows the event loop to execute one or more other coroutines that may have been scheduled or suspended.
It is commonly used right after scheduling one or more coroutines as tasks, such as after calling asyncio.create_task() or calling asyncio.gather().
The example below demonstrates this.
A task is created which schedules it for execution. The calling coroutine then sleeps for zero seconds, which allows the scheduled task to begin running and sleep itself.
The main coroutine resumes then awaits the task to complete entirely.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# SuperFastPython.com # example of sleeping for zero seconds import asyncio # custom coroutine async def custom_coro(): # report a message print('task running') # block for a moment await asyncio.sleep(1) # report a message print('task done') # entry point coroutine async def main(): # execute another coroutine task = asyncio.create_task(custom_coro()) print('main is blocking now') # sleep while the task is running await asyncio.sleep(0) # report a message print('main is done blocking') # wait for the task to complete await task # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then creates our custom coroutine and schedules it as a task. It then sleeps for zero seconds and allows the scheduled task to begin executing.
The task begins, first reporting a message, then sleeping for one second.
The main() coroutine resumes, ropers a message of its own, and then suspends again, awaiting the task.
The task resumes, reports a message then terminates.
The example highlights how we might allow scheduled tasks to begin their execution and interleave their execution with other tasks.
1 2 3 4 |
main is blocking now task running main is done blocking task done |
Next, let’s explore how we might create a periodic task using the sleep() function.
Example of Periodic Task with asyncio.sleep()
The asyncio.sleep() function can be used to create a task that executes periodically.
This can be achieved by creating a loop that runs forever, or a fixed time, that performs a custom operation each iteration and then sleeps for a fixed interval.
This way, a custom operation can be performed every fraction of a second, every second, every minute, or whatever time interval is required for the application.
The example below demonstrates this.
A periodic task is defined that runs every 200 milliseconds. The operation performed is to just report a message, but it could be anything you like, such as checking a resource or refreshing some data.
The main coroutine creates and schedules the task and then simulates going on with other tasks before finally terminating the program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# SuperFastPython.com # example of a sleep in a periodic task import asyncio # periodic task async def periodic(): # loop forever while True: # perform operation print('>task is running') # block for an interval await asyncio.sleep(0.2) # entry point coroutine async def main(): # report a message print('Main is starting') # start the periodic task _ = asyncio.create_task(periodic()) # report a message print('Main is resuming with work...') # wait a while for some reason await asyncio.sleep(3) # report a message print('Main is done') # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then creates and schedules the periodic task.
It then continues on with its simulated work, choosing to report a message and sleep for a while.
The task runs, looping forever.
Each iteration it performs its task which is to report a message and then sleeps for about 200 milliseconds.
The main() coroutine resumes after 3 seconds or about 15-16 periods of the task and reports a final message before terminating the application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Main is starting Main is resuming with work... >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running >task is running Main is done |
Next, let’s look at canceling a sleep operation.
Example of Interrupting a Sleep
Although an asyncio coroutine is blocked while sleeping, it can be interrupted.
A common way to interrupt a sleep is by canceling the task.
Recall that a task can be created that wraps a coroutine, which allows the task to be scheduled and executed independent of the current coroutine, but also provides a handle on the task. The cancel() method can be called on the task.
You can learn more about canceling an asyncio task in the tutorial:
This will raise a CancelledError exception in the task. If the task is sleeping the CancelledError exception is still raised.
This allows one coroutine to wake a task from sleep.
The woken task can then choose to terminate, e.g. let the CancelledError exception terminate the task, or it can catch the CancelledError exception, handle it, and continue on with its own operations.
The example below demonstrates the ability to wake a task up from sleep.
A task is created and scheduled that sleeps for a number of seconds.
The main coroutine then waits a moment before canceling the task, waking it from its sleep.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# SuperFastPython.com # example of canceling a sleep import asyncio # custom coroutine async def custom_coro(): # report a message print('task running') try: # block for a moment await asyncio.sleep(60) except asyncio.CancelledError: print('task woken up') # report a message print('task done') # entry point coroutine async def main(): # execute another task task = asyncio.create_task(custom_coro()) # allow the task to run for a moment await asyncio.sleep(2) # report a message print('canceling the task') # canceling the task task.cancel() # wait a moment await asyncio.sleep(1) # report a message print('main is done') # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine runs first creating and scheduling the custom coroutine as a task.
It then sleeps for 2 seconds, to allow the task to start running.
The task runs, first reporting a message then sleeping for one minute.
The main() coroutine runs again and cancels the task. It then sleeps for one second, suspending and allowing the task to respond to the request to cancel.
The task resumes and responds to the cancellation. The sleep is broken and a CancelledError is raised. The task handles the exception and reports that it was woken from its sleep.
The task reports a final message and then terminates.
The main() coroutine resumes and then terminates the program.
This highlights how we can wake a task from a sleep and generally interrupt sleep operations.
1 2 3 4 5 |
task running canceling the task task woken up task done main is done |
Next, let’s look at what happens if we do not use the sleep() function correctly.
Example of Not Awaiting asyncio.sleep()
Recall that the sleep() function returns a coroutine.
To use the sleep() function correctly, the coroutine that is returned must be awaited.
If it is not awaited, then the calling coroutine will not block and a warning message will be reported indicating that a coroutine was created but never awaited.
This can happen if the sleep coroutine is assigned and never awaited.
It can also happen if the await expression is forgotten as a prefix for the call to the sleep() function.
The example below demonstrates this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
# SuperFastPython.com # example of not awaiting a sleep import asyncio # custom coroutine async def custom_coro(): # report a message print('coroutine running') # block for a moment (forget to await) asyncio.sleep(1) # report a message print('coroutine done') # entry point coroutine async def main(): # execute another coroutine await custom_coro() # start the asyncio program asyncio.run(main()) |
Running the example first creates the main() coroutine and uses it as the entry point to the application.
The main() coroutine then creates the custom coroutine and awaits it.
The custom coroutine runs. It reports a message and calls sleep. A sleep coroutine is returned but is not assigned and is not awaited.
The task reports a second message and terminates, then the main() coroutine terminates.
The sleep coroutine is never used and a RuntimeWarning is reported highlighting that the sleep coroutine was never awaited.
This highlights the common problem of forgetting to await the sleep coroutine object.
1 2 3 4 5 |
coroutine running ...: RuntimeWarning: coroutine 'sleep' was never awaited asyncio.sleep(1) RuntimeWarning: Enable tracemalloc to get the object allocation traceback coroutine done |
The fix is easy, simply add the await expression to the call to sleep().
For example:
1 2 3 |
... # block for a moment await asyncio.sleep(1) |
Further Reading
This section provides additional resources that you may find helpful.
Python Asyncio Books
- Python Asyncio Mastery, Jason Brownlee (my book!)
- Python Asyncio Jump-Start, Jason Brownlee.
- Python Asyncio Interview Questions, Jason Brownlee.
- Asyncio Module API Cheat Sheet
I also recommend the following books:
- Python Concurrency with asyncio, Matthew Fowler, 2022.
- Using Asyncio in Python, Caleb Hattingh, 2020.
- asyncio Recipes, Mohamed Mustapha Tahrioui, 2019.
Guides
APIs
- asyncio — Asynchronous I/O
- Asyncio Coroutines and Tasks
- Asyncio Streams
- Asyncio Subprocesses
- Asyncio Queues
- Asyncio Synchronization Primitives
References
Takeaways
You now know how to use the asyncio.sleep() function in Python.
Do you have any questions?
Ask your questions in the comments below and I will do my best to answer.
Photo by Brayden Law on Unsplash
Do you have any questions?