Why Does time.sleep() Break Python Async Code? Fix Event Loop Blocking
Problem
When I wrote an async Python application, I used time.sleep() for delays. The entire application became frozen and unresponsive. Other async tasks stopped running during the sleep period.
Here’s what I saw:
Task 1 starting...[Application frozen for 1 second - no other output]Task 1 completed.Task 2 starting...[Application frozen again]Environment
- Python 3.11
- asyncio for async programming
- Ubuntu 22.04
What happened?
I wrote an async function that uses time.sleep() for a delay:
import asyncioimport time
async def task(name): print(f"{name} starting...") time.sleep(1) # This is the problem! print(f"{name} completed.")
async def main(): # Two tasks running concurrently await asyncio.gather(task("Task 1"), task("Task 2"))
asyncio.run(main())I expected both tasks to run concurrently. But when I ran this:
python bad_sleep_example.pyI got sequential output:
Task 1 starting...[1 second pause - nothing else happens]Task 1 completed.Task 2 starting...[1 second pause]Task 2 completed.Both tasks ran one after another, not concurrently. The async code behaved like synchronous code.
How to solve it?
I replaced time.sleep() with asyncio.sleep():
import asyncio
async def task(name): print(f"{name} starting...") await asyncio.sleep(1) # Non-blocking! print(f"{name} completed.")
async def main(): await asyncio.gather(task("Task 1"), task("Task 2"))
asyncio.run(main())Now I run the script again:
python good_sleep_example.pyI get concurrent execution:
Task 1 starting...Task 2 starting...[Both tasks wait together]Task 1 completed.Task 2 completed.Both tasks started at the same time. They completed together after 1 second, not 2 seconds.
The reason
I think the key reason is how Python’s event loop works:
-
time.sleep()is synchronous - It blocks the entire thread. The event loop cannot run any other task while sleeping. Everything stops. -
asyncio.sleep()is asynchronous - It returns control to the event loop. The event loop can run other tasks while waiting. Concurrency continues.
time.sleep(1):┌──────────────────────────────────────┐│ Thread BLOCKED for 1 second ││ No other tasks can run │└──────────────────────────────────────┘
asyncio.sleep(1):┌────────────┐ ┌────────────┐│ Task 1 │ │ Task 2 ││ yields │ ──→ │ runs while ││ control │ │ Task 1 │└────────────┘ │ waits │ └────────────┘This distinction matters for async web servers handling thousands of requests. Blocking the event loop makes all users wait, not just the one request using time.sleep().
Common mistakes to avoid
I also found other blocking operations that cause the same problem:
import asyncioimport requests # Sync HTTP clientimport os
async def bad_example(): # All of these BLOCK the event loop: time.sleep(1) # Blocking sleep requests.get(url) # Blocking HTTP request os.system("ls") # Blocking system call open("file.txt").read() # Blocking file I/OFor each blocking operation, there’s an async alternative:
import asyncioimport aiohttp # Async HTTP clientimport aiofiles # Async file I/O
async def good_example(): await asyncio.sleep(1) # Non-blocking sleep async with aiohttp.ClientSession() as session: await session.get(url) # Non-blocking HTTP await asyncio.to_thread(os.system, "ls") # Offload to thread async with aiofiles.open("file.txt") as f: await f.read() # Non-blocking file I/OSummary
In this post, I showed why time.sleep() breaks async Python code. The key point is: time.sleep() blocks the entire event loop, while asyncio.sleep() yields control to other tasks. Always use async alternatives for I/O operations in async code.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments