async/await overlaps I/O waits on one thread—concurrency without threads.
While one task is waiting for the network, let another one run. Not more workers — one worker that stops twiddling its thumbs.
Almost every AI application is IO-bound on model calls, and this is the difference between 40 sequential requests and 40 concurrent ones.
An async def defines a coroutine that runs on a single-threaded event loop. await suspends the coroutine at an I/O point and hands control back to the loop, which runs whichever coroutine is ready. This overlaps many I/O waits concurrently without threads—but gives no speedup for CPU-bound work.
Async lets one thread juggle many I/O-bound tasks. An async def is a coroutine; await marks a point where it suspends and returns control to the event loop, which then runs other ready coroutines. asyncio.gather launches many at once so their waits overlap instead of running one after another. It shines for network, disk, and DB calls, but does nothing for CPU-bound work—there you need multiprocessing.
AsyncIO, await, and async - Concurrency in Python — Socratica, 9:12