Assume I want to implement the following using asyncio:
def f():
val1 = a() # a() takes 1 sec
val2 = b() # b() takes 3 sec
val3 = c(val1, val2) # c() takes 1 sec, but must wait for a() and b() to finish
val4 = d(val1) # d() takes 1 sec, but must wait for a() to finish
all functions a, b, c, d are asynchronous and could potentially run in parallel. The optimized way to run this would be: 1) run a() and b() in parallel. 2) when a() is done, run d(). 3) when a() and b() are done, run c(). Everything together should take 4 seconds.
I find that implementing that with asyncio is not ideal:
import time
import asyncio
async def a():
await asyncio.sleep(1)
async def b():
await asyncio.sleep(3)
async def c(val1, val2):
await val2
await asyncio.sleep(1)
async def d(val1):
await val1
await asyncio.sleep(1)
async def f():
val1 = a()
val2 = b()
val3 = c(val1, val2)
val4 = d(val1)
return await asyncio.gather(val3, val4)
t1 = time.time()
await f()
t2 = time.time()
print(t2 - t1) # This will be 4 seconds indeed
The above implementation works, but the main flow is that I need to know that a() finishes before b(), in order to await val1 in d() and not await it in c(). In other words, given a (possibly complex) execution graph, I have to know which functions finish before others, in order to place the "await" statement in the right place. It I await the same coroutine in two places, I get an exception.
My question is the following: is there a mechanism in asyncio (or other python module), that awaits on coroutines automatically, just when they are needed to be resolved to actual values? I know that such mechanism is implemented in other parallel execution mechanisms.