I need function_B
to wait for function_A
. The wait_for_action_to_be_done()
function does the job but I would like something more convenient with wait_for(action_to_wait)
. The issue is that, in wait_for(action_to_wait)
the action_to_wait variable in the condition in the while loop is never updated. I guess something like a pointer would help.
import asyncio
action_done = False
async def function_A(delay):
global action_done
while True:
await asyncio.sleep(delay)
action_done = True
break
async def wait_for(action_to_wait):
print("wait for action to be done...")
while not action_to_wait:
await asyncio.sleep(0.1)
async def wait_for_action_to_be_done():
print("wait for action to be done...")
while not action_done:
await asyncio.sleep(0.1)
async def function_B():
await wait_for_action_to_be_done() # OK, works
# await wait_for(action_done) # KO, infinite while loop as the condition is never updated
print("performing next action...")
async def main():
await asyncio.gather(function_A(2), function_B())
asyncio.run(main())
async def wait_for(action_to_wait):
print("wait for action to be done...")
while not action_to_wait:
await asyncio.sleep(0.1)
while not action_to_wait:
waits for a local variable to change, it will never change. once you bind the global to a local variable, the local variable doesn't change when the global one does, see Facts and myths about Python names and values.
You need to wait for a non-local object to change, the easiest way is to use a lambda. (or nonlocal
with an inline function).
async def wait_for(action_to_wait):
print("wait for action to be done...")
while not action_to_wait():
await asyncio.sleep(0.1)
async def function_B():
# await wait_for_action_to_be_done() # OK, works
await wait_for(lambda: action_done) # works!
print("performing next action...")
Lastly using await asyncio.sleep(0.1)
is fundamentally wrong, you should be using asyncio.Event
instead.
import asyncio
async def function_A(delay, wait_event: asyncio.Event):
while True:
await asyncio.sleep(delay)
wait_event.set()
break
async def wait_for(wait_event: asyncio.Event):
print("wait for action to be done...")
await wait_event.wait()
async def function_B(wait_event: asyncio.Event):
await wait_event.wait() # works
await wait_for(wait_event) # also works
print("performing next action...")
async def main():
wait_event = asyncio.Event()
await asyncio.gather(function_A(2, wait_event), function_B(wait_event))
asyncio.run(main())
The rule of thumb is that if you are using a sleep then you are likely doing something wrong, sleeps should only be used for inserting a real life delay or a timer, not waiting for things to happen.
If you are waiting for an event to happen from another thread you should be using a mixture of threading.Event
and await loop.run_in_executor
to wait for it, as asyncio.Event
can only be used from inside the eventloop (not thread-safe).
note that you can simulate what you know about pointers in other languages using an object
or a list or a dictionary or a dummy class.
class PackedBool:
def __init__(self, value: bool):
self.value = value
action_done = PackedBool(False) # access using action_done.value
this is largely unnecessary for a bool where we already have the far superior events.