Skip to content

Tasks (create_task) and gather

A Task is a scheduled coroutine.

  • You create it
  • The event loop runs it concurrently with others
create_task.py
import asyncio
 
 
async def job(i: int) -> str:
    await asyncio.sleep(0.2)
    return f"done {i}"
 
 
async def main():
    t1 = asyncio.create_task(job(1))
    t2 = asyncio.create_task(job(2))
 
    # await results
    print(await t1)
    print(await t2)
 
 
asyncio.run(main())
sketch create_task schedules; it does not start p5.js
A measured surprise: with two tasks created and a print between, the print runs FIRST. create_task hands the coroutine to the loop and returns immediately -- the coroutine body does not begin until the loop next gets control, which is at your first await. Observed order was: after create_task, start 1, start 2, end 1, end 2.

gather is a convenient way to run many coroutines and collect results.

gather.py
import asyncio
 
 
async def job(i: int) -> int:
    await asyncio.sleep(0.1)
    return i * i
 
 
async def main():
    results = await asyncio.gather(*(job(i) for i in range(10)))
    print(results)
 
 
asyncio.run(main())
sketch Sequential await against gather, with the measured times p5.js
Ten jobs, each awaiting asyncio.sleep(0.1). Awaiting them one at a time takes the sum; handing them all to asyncio.gather takes about as long as the slowest one. Measured on CPython 3.14.4: 1.091 s sequential against 0.109 s gathered, a 10x difference, and both produce exactly the same list. The bars run in real time -- the animation is not sped up.

If one coroutine fails, gather cancels others by default.

You can keep errors as values:

gather_return_exceptions.py
import asyncio
 
 
async def ok():
    return 1
 
 
async def bad():
    raise ValueError("boom")
 
 
async def main():
    res = await asyncio.gather(ok(), bad(), return_exceptions=True)
    print(res)
 
 
asyncio.run(main())

pch.coffeeTagline

pch.coffeeCta

pch.feedbackHeading

pch.feedbackSubheading