Basic

Waiting without blocking

A wait should not stop the rest of the program. asyncio keeps several waits open at once, so the time costs the slowest one instead of the sum of all of them.

What you will see

ok

About 2 min of reading

Quick check

Before this new lesson, one small question about the lesson you just finished. Recall it, do not look it up.

Try It

Check what overlapping waits cost.

1

Sum of waits.

waits = [1, 2, 3] print(sum(waits))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
6
2

Slowest wait.

waits = [1, 2, 3] print(max(waits))

Before you run it, say out loud what it will print. Then open the answer.

What it prints
3
3

Gather sums.

import asyncio async def one(): return 1 async def main(): r = await asyncio.gather(one(), one()) print(sum(r)) asyncio.run(main())

Before you run it, say out loud what it will print. Then open the answer.

What it prints
2
4

Task started.

import asyncio async def main(): t = asyncio.create_task(asyncio.sleep(0)) await t print("ok") asyncio.run(main())

Before you run it, say out loud what it will print. Then open the answer.

What it prints
ok

Put it in order

Before you write anything, put these lines back in the right order. Click a line to move it up one step. This support step is easier than writing code from scratch, and it is here to make the last example stick.

Data Flow

The work is started, the loop keeps it open, another task runs while it waits, and the value is collected once it is ready.
1 start the work
↓
2 keep the task open
↓
3 other tasks run
↓
4 collect the value
Line by line

Read the first example one line at a time. A line that prints shows what appears on screen; the rest set things up quietly.

waits = [1, 2, 3] sets nothing on screen
print(sum(waits)) 6
Why this works
Start before you wait

The gap that matters is between starting the work and waiting for it. Start everything first, then wait, and the waits overlap.

A plain call is a wall

Any blocking call inside an async function freezes the loop. Push that work to a task or run it in a thread of its own.

Examples

Two waits in a row

import asyncio

async def one():
    await asyncio.sleep(1)
    return 1

async def main():
    a = await one()
    b = await one()
    print(a + b)

asyncio.run(main())

Each await waits for the one before it, so two sleeps pay twice.

Both at once

import asyncio

async def one():
    await asyncio.sleep(1)
    return 1

async def main():
    results = await asyncio.gather(one(), one())
    print(sum(results))

asyncio.run(main())

gather starts both tasks before either finishes, so the pair pays one sleep.

A blocking call inside

import time

def slow():
    time.sleep(1)
    return 1

A plain sleep holds the whole process. Inside an async function it stops every other task from running.

Start then await

import asyncio

async def main():
    task = asyncio.create_task(asyncio.sleep(1))
    await asyncio.sleep(1)
    await task

asyncio.run(main())

create_task starts the work and returns right away. The await later only waits for what is left.

A task you never await

import asyncio

async def main():
    asyncio.create_task(asyncio.sleep(1))
    await asyncio.sleep(0)

asyncio.run(main())

A task nobody awaits still runs, but its errors are swallowed. Keep a handle or let it go on purpose.

Count the waits

waits = [1, 2, 3]
print(sum(waits))
print(max(waits))

Serial waits pay the sum. Concurrent waits pay the maximum, which is the number you want.

Words this lesson uses

Each one is defined here on this site. Open the entry for the full definition and the other words it points at.

← Handing work over
Previous Lesson
Handing work over
Next Lesson
await and return
await and return →