Asyncio sleep() in Python

November 22, 2022 Python Asyncio

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:

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.

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:

...
# 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:

...
# 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:

...
# 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:

...
# 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.

...
# 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:

...
# 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:

...
# 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:

...
# 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:

...
# 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:

...
# 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.

asyncio.sleep() vs time.sleep()

Another sleep function in Python is provided by the time module called time.sleep().

For example:

...
# 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.

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.

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.

Now that we know all about the asyncio.sleep() function, let's look at some worked examples.

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.

# 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.

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.

# 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.

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.

# 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.

<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:

...
# 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.

# 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.

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.

# 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.

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.

# 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.

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.

# 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.

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:

...
# block for a moment
await asyncio.sleep(1)

Takeaways

You now know how to use the asyncio.sleep() function in Python.



If you enjoyed this tutorial, you will love my book: Python Asyncio Jump-Start. It covers everything you need to master the topic with hands-on examples and clear explanations.