async and await
async and await are the keywords for code that waits: async def defines a coroutine function, and await runs a coroutine and waits for its result.
Last updated: 30 Sep, 2026 · Python 3.14
A real model takes a second or more to answer, and nearly all of that time your program is waiting on the network. This lesson measures that wait, then writes the version that can share it.
Syntax:
import asyncio
async def work():
await asyncio.sleep(1) # wait without blocking
asyncio.run(work()) # start from ordinary codeTiming three slow calls
import time
def ask_model_slowly(text):
time.sleep(0.5)
return f"answer to: {text}"
start = time.perf_counter()
for text in ["ticket 1", "ticket 2", "ticket 3"]:
print(ask_model_slowly(text))
print(f"took {time.perf_counter() - start:.1f} seconds")answer to: ticket 1 answer to: ticket 2 answer to: ticket 3 took 1.5 seconds
time.sleep(0.5) pauses for half a second, standing in for the network. time.perf_counter reads a clock, and :.1f in the f-string shows one digit after the point.
Three calls, half a second each, one after the other: a second and a half. A hundred tickets would take fifty seconds, nearly all of it waiting.
Writing the async version
import asyncio
async def ask_model_slowly(text):
await asyncio.sleep(0.5)
return f"answer to: {text}"
async def main():
for text in ["ticket 1", "ticket 2", "ticket 3"]:
print(await ask_model_slowly(text))
asyncio.run(main())answer to: ticket 1 answer to: ticket 2 answer to: ticket 3
async def makes a coroutine function: calling it does not run it straight away. await runs it and waits for the result, and while it waits, Python is free to work on something else.
await only works inside an async def. asyncio.run(main()) is the one line that starts the async part of a program from ordinary code.
This version is not faster yet. It still awaits one ticket before starting the next. What changed is that the waiting can now be shared, which asyncio.gather does.
Forgetting await
async def main():
answer = ask_model_slowly("ticket 1")
print(answer)
asyncio.run(main())<coroutine object ask_model_slowly at 0x10ace8fb0>
There is no answer and no error, only a coroutine object: the work that was never started. Python also prints a RuntimeWarning: coroutine ... was never awaited to the error stream. When an async function seems to return something strange, look for a missing await first.
def vs async def
def | async def | |
|---|---|---|
| Calling it | Runs it and gives the result | Gives a coroutine object |
| Getting the result | The call itself | await the call |
| Waiting inside it | time.sleep blocks everything | await asyncio.sleep lets others run |
Where async shows up in AI code
- Model SDKs offer async clients, such as
AsyncOpenAI, whose calls youawait. - Agent frameworks run tools and model calls as coroutines.
- Web servers such as FastAPI handle each request with
async def.
time.sleep inside an async def still blocks the whole program. Use await asyncio.sleep in async code, or the waits cannot overlap.Related
- Previous: model_validate_json
- Next: asyncio.gather
- Reference: Coroutines and Tasks in the Python docs
- Time the async version with
time.perf_counterand confirm it takes about as long as the first one. - Change the sleep to
1second and add a fourth ticket. Work out the time before you run it. - Add
awaitback in front ofask_model_slowlyin the last example.
You understood something today that you didn't yesterday.