In the async joke example (Example 4-4), why does the answer 'Timing!' appear immediately after the question, even though q() has an asyncio.sleep(3)?
The answer appears immediately because q() suspends at await asyncio.sleep(3), handing control back to the event loop instead of blocking. asyncio.gather() then lets a() run during q()'s three-second sleep, so "Timing!" prints right away and q() resumes only after the timer completes.
In Example 4-4, q() is an async function. When it calls await asyncio.sleep(3), it does not block the whole program. Instead, it yields control to the event loop, which can run other scheduled coroutines. Because main() uses asyncio.gather(q(), a()), a() is scheduled and can print "Timing!" while the event loop is still waiting out q()'s three-second sleep. The book describes this as the event loop setting a stopwatch for q(), running a() in the meantime, and only returning to q() when no other work remains, leaving the loop to stare at the rest of the three seconds. This cooperative yielding is what makes the async version feel like a programmer telling the joke, unlike the synchronous version where time.sleep blocks and delays a() by three seconds.
Key points
- q() prints its question first, then reaches await asyncio.sleep(3).
- The await suspends q() and returns control to the event loop rather than blocking.
- asyncio.gather(q(), a()) schedules a() to run concurrently during q()'s sleep.
- a() prints "Timing!" immediately, and q() finishes only after the three-second delay ends.
- The same async behavior models slow operations like file reads or network access.
Related questions
FastAPI: Modern Python Web Development
Bill Lubanovic;
First Edition · O'Reilly Media, Inc.