Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What would be Promise.race equivalent in Python asynchronous code?

I'd like to reproduce javascript's Promise.race behavior in Python code. I want to run group of coroutines simultaneously and return when first one is done, get its result and cancel/discard results of the ones that are still running.

like image 566
Dreaded Singleton Avatar asked Dec 29 '18 06:12

Dreaded Singleton


1 Answers

You can use asyncio.wait with the argument return_when set to FIRST_COMPLETED. The example code below will print 1 and the exception will never be raised. The second for loop makes sure all of our pending coroutines are properly finished. If raising_wait coroutine finishes first, after calling the result method of the created Task object, the exception will be raised as specified in documentation. Last thing to mention is that, that using asyncio.wait with FIRST_COMPLETED doesn't guarantee that we will have exactly one Task in done set if the couroutines finish in almost same time.

from contextlib import suppress
import asyncio


async def wait(t):
    await asyncio.sleep(t)
    return t


async def raising_wait(t):
    await asyncio.sleep(t)
    raise TimeoutError("You waited for too long, pal")


loop = asyncio.new_event_loop()
task_set = set()

task_set.add(loop.create_task(wait(1)))
task_set.add(loop.create_task(raising_wait(2)))
task_set.add(loop.create_task(wait(3)))

done_first, pending = loop.run_until_complete(
    asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED)
)
for coro in done_first:
    try:
        print(coro.result())
    except TimeoutError:
        print("cleanup after error before exit")

for p in pending:
    p.cancel()
    with suppress(asyncio.CancelledError):
        loop.run_until_complete(p)

loop.close()
like image 60
Dreaded Singleton Avatar answered Oct 13 '22 07:10

Dreaded Singleton