Basic

Order is not finished order

When work runs at the same time, the order it finishes in is not the order you started it. Anything that reads a shared value has to survive that shuffle.

What you will see

a2 b1

About 3 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 an unordered finish changes.

1

Start order kept.

import asyncio async def label(t, w): await asyncio.sleep(w) return t async def main(): r = await asyncio.gather(label("slow", 1), label("fast", 0)) print(" ".join(r)) asyncio.run(main())

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

What it prints
slow fast
2

Sorted report.

import asyncio async def one(k): await asyncio.sleep(0) return k.upper() async def main(): r = await asyncio.gather(one("b"), one("a")) print(" ".join(sorted(r))) asyncio.run(main())

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

What it prints
A B
3

Keys survive.

import asyncio async def one(i): await asyncio.sleep(0) return i * 10 async def main(): r = await asyncio.gather(*[one(i) for i in range(3)]) print(" ".join(str(x) for x in r)) asyncio.run(main())

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

What it prints
0 10 20
4

Zip pairs.

names = ["a", "b"] values = [2, 1] out = [n + str(v) for n, v in zip(names, values)] print(" ".join(out))

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

What it prints
a2 b1

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

Tasks are started together, they finish in whatever order timing gives, results are matched by key, and only the report is sorted.
1 start the tasks
↓
2 finish in any order
↓
3 match by key
↓
4 sort the report
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.

import asyncio sets nothing on screen
async def label(t, w): sets nothing on screen
await asyncio.sleep(w) sets nothing on screen
return t sets nothing on screen
async def main(): sets nothing on screen
r = await asyncio.gather(label("slow", 1), label("fast", 0)) sets nothing on screen
print(" ".join(r)) slow fast
asyncio.run(main()) sets nothing on screen
Why this works
Carry the key

Never identify a result by where it landed. Put the key inside the value, and a changed finish order cannot mislabel anything.

Sort at the edge

Output that a person reads should be sorted. The work itself should not care about order at all, which is what makes it parallel.

Examples

Start order kept

import asyncio

async def label(text, wait):
    await asyncio.sleep(wait)
    return text

async def main():
    r = await asyncio.gather(label("slow", 2), label("fast", 1))
    print(" ".join(r))

asyncio.run(main())

gather returns results in the order the tasks were started, whatever the finish order was.

Finish order seen

import asyncio

async def label(text, wait):
    await asyncio.sleep(wait)
    return text

async def main():
    tasks = [label("slow", 2), label("fast", 1)]
    for t in asyncio.as_completed(tasks):
        print(await t)

asyncio.run(main())

as_completed reports the order things actually finished, which changes with timing.

A log of arrivals

import asyncio

log = []

async def note(text, wait):
    await asyncio.sleep(wait)
    log.append(text)

async def main():
    await asyncio.gather(note("slow", 2), note("fast", 1))
    print(" ".join(log))

asyncio.run(main())

A shared list records finish order, so the same code can print two different lines.

Index instead of position

import asyncio

async def one(i):
    await asyncio.sleep(3 - i)
    return i

async def main():
    r = await asyncio.gather(*[one(i) for i in range(3)])
    print(" ".join(str(x) for x in r))

asyncio.run(main())

Each result carries its own key, so a shuffled finish order cannot mislabel a value.

A map of results

import asyncio

async def one(key):
    await asyncio.sleep(0)
    return key.upper()

async def main():
    r = await asyncio.gather(one("a"), one("b"))
    print(" ".join(sorted(r)))

asyncio.run(main())

Sorting before printing removes the order you cannot control. Do that in the report, not in the work.

Zip keeps pairs

names = ["a", "b", "c"]
values = [3, 1, 2]
for n, v in zip(names, values):
    print(n, v)

A result list lines up with the list you started from, so pairing by index survives the shuffle.

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.

← await and return
Previous Lesson
await and return
Next Lesson
Two writers, one value
Two writers, one value →