Processing results as they finish

asyncio · Advanced · 9 min

Showing the fastest answer first, or racing replicas, needs completion order rather than argument order.

gather gives you results in argument order only after everything finishes. Often you want each result as soon as it is ready.

asyncio.as_completed(aws) yields results in completion order. In Python 3.13 it is also an async iterator that yields the original tasks, which makes it easy to know which input finished:

async for task in asyncio.as_completed(tasks):
    print(task.get_name(), await task)

The classic form still works: for fut in as_completed(aws): result = await fut, where each fut is a new awaitable, not your original task. A timeout= argument raises TimeoutError if everything isn't done in time.

asyncio.wait(tasks, return_when=...) is the lower-level tool. It returns two sets, (done, pending), and never raises the tasks' exceptions itself:

  • FIRST_COMPLETED for racing: take the first winner, then cancel the pending ones.
  • FIRST_EXCEPTION and ALL_COMPLETED (the default).

wait only accepts tasks and futures, not bare coroutines. Remember to cancel or await whatever is left in pending, or you leak tasks.

Example

import asyncio

async def replica(name, delay):
    await asyncio.sleep(delay)
    return name

async def main():
    tasks = [asyncio.create_task(replica(n, d), name=n)
             for n, d in [("eu", 0.3), ("us", 0.1), ("asia", 0.2)]]
    async for t in asyncio.as_completed(tasks):   # 3.13+
        print("finished:", await t)               # us, asia, eu

    racers = [asyncio.create_task(replica(n, d)) for n, d in [("a", 0.2), ("b", 0.05)]]
    done, pending = await asyncio.wait(racers, return_when=asyncio.FIRST_COMPLETED)
    print("winner:", done.pop().result(), "| pending:", len(pending))
    for p in pending:
        p.cancel()

asyncio.run(main())
# finished: us / finished: asia / finished: eu
# winner: b | pending: 1

Remember

  • as_completed yields in completion order; in 3.13 async for gives the original tasks
  • wait() returns (done, pending) sets and doesn't raise task errors
  • Cancel the pending tasks after a race

Watch out for

  • Passing bare coroutines to asyncio.wait; wrap them in tasks first.

Check yourself

  1. asyncio.wait(tasks, return_when=FIRST_COMPLETED) returns. What should you usually do with pending?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/asyncio-task.html#asyncio.as_completed

Builds on

Unlocks

Nothing builds on this yet.