This rule raises an issue when the sleep function is used in an asynchronous loop instead of an Event object.
Asynchronous tasks often need to wait for a condition to change or an event to occur. A simple-looking but inefficient way to achieve this is by
polling the condition within a loop, using sleep to pause between checks:
while not condition_is_met:
await asyncio.sleep(0.1) # Noncompliant
# Condition is met, we can proceed
This busy-waiting approach is problematic in asynchronous code because it introduces increased latency. The task only notices the condition change
after the sleep interval expires. If the condition becomes true just after the task starts sleeping, the reaction is delayed.
Instead of polling with sleep, use dedicated synchronization primitives like asyncio.Event, trio.Event or
anyio.Event. Using an Event allows a task to efficiently pause (await event.wait()) until another part of the
program signals the event (event.set()). The waiting task consumes almost no resources while paused and reacts immediately when the event
is set.
import asyncio
SHARED_CONDITION = False
async def worker():
while not SHARED_CONDITION: # Noncompliant
await asyncio.sleep(0.01)
print("Condition is now true")
asyncio.run(worker)
import asyncio
SHARED_CONDITION = asyncio.Event()
async def worker():
await SHARED_CONDITION.wait() # Compliant
print("Condition is now true")
asyncio.run(worker)
import trio
SHARED_CONDITION = False
async def worker():
while not SHARED_CONDITION: # Noncompliant
await trio.sleep(0.01)
print("Condition is now true")
trio.run(worker)
import trio
SHARED_CONDITION = trio.Event()
async def worker():
await SHARED_CONDITION.wait() # Compliant
print("Condition is now true")
trio.run(worker)
import anyio
SHARED_CONDITION = False
async def worker():
while not SHARED_CONDITION: # Noncompliant
await anyio.sleep(0.01)
print("Condition is now true")
anyio.run(worker)
import anyio
SHARED_CONDITION = anyio.Event()
async def worker():
await SHARED_CONDITION.wait() # Compliant
print("Condition is now true")
anyio.run(worker)