How does Python's asyncio event loop work, and when do you prefer it over threading?
py-sen-001
Your answer
Answer as you would in a real interview — explain your thinking, not just the conclusion.
Model answer
asyncio uses single-threaded cooperative multitasking. The event loop maintains a run queue of coroutines. When a coroutine reaches an await expression, it suspends and returns control to the loop, which resumes the next ready coroutine. There is no parallelism — concurrency comes from overlapping I/O waits. This avoids thread-safety complexity and context-switching overhead at scale. I prefer asyncio when the bottleneck is I/O (database queries, HTTP calls, Redis) and I need to handle thousands of concurrent connections — typical for Python API servers. I choose threading for blocking third-party libraries that do not support async. I use multiprocessing for CPU-bound work.
Code example
import asyncio
import aiohttp
async def fetch(session, url):
async with session.get(url) as resp:
return await resp.text()
async def main(urls):
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
# All I/O runs concurrently on a single thread
return await asyncio.gather(*tasks)
# Run a blocking call without blocking the loop
async def run_blocking():
loop = asyncio.get_running_loop()
result = await loop.run_in_executor(None, some_sync_function)
return result
Follow-up
How do you run a blocking synchronous function inside an asyncio event loop without blocking the loop? What is the trade-off versus spawning a new process?