Asyncio
28 bites tagged Asyncio — interview questions with model answers, and 60-second explainers.
asyncio.gather vs asyncio.wait
Gather returns ordered results and propagates the first exception (or captures them); wait returns done/pending sets and never raises, you inspect each. Whether you know how each aggregates results and handles errors.
How do you handle slow startup without blocking the FastAPI event loop?
It tests FastAPI lifespan events and event loop hygiene. Use an async lifespan to offload blocking model loading to a thread pool, track readiness with a global flag, and return 503 for early requests. Never block the event loop in startup handlers.
Why synchronous DB libraries block async FastAPI endpoints and correct SQLAlchemy usage
This tests event loop blocking: sync DB calls in async def halt all requests. Answer: sync drivers block the loop despite releasing the GIL; use asyncpg with SQLAlchemy create_async_engine and AsyncSession. Red flag: recommending run_in_executor as default.
Why are contextvars better than threading.local in async Python?
This tests whether you know async tasks share OS threads, making thread-local storage unsafe for request state. A great answer notes ContextVar is task-local and resets automatically, while threading.local bleeds across concurrent coroutines.
How do you gracefully cancel and clean up an asyncio task?
This tests asyncio cooperative cancellation and cleanup. A strong answer covers catching CancelledError at await points, using try/finally or async context managers for cleanup, and re-raising.
Unhandled exception in asyncio.create_task(): consequence and detection
Tests Task exception capture vs propagation. Good answer: exceptions are stored in the Task object, the loop keeps running, and the creator must await the task or call task.exception() to retrieve it; unretrieved ones may be logged.
When should you use asyncio.Lock over threading.Lock?
This tests cooperative multitasking knowledge: asyncio.Lock yields to the event loop via await, while threading.Lock blocks the OS thread and freezes the loop. A red flag is claiming threading.Lock works because locks are universal.
Explain the asyncio event loop and cooperative multitasking
Tests if you view the event loop as a single-threaded orchestrator, not magic parallelism. Strong answers note it runs tasks and callbacks, manages a ready queue, and yields control at await. Red flag: calling it multithreading or parallel execution.
How do you safely execute blocking code from an async function
Tests whether you know how to prevent event loop blocking by offloading sync work. Name asyncio.to_thread or run_in_executor, explain ThreadPoolExecutor scheduling, and note when processes beat threads.
Explain await's purpose, awaitable types, and event loop signaling
This tests whether you understand await as a yield point. A strong answer lists three awaitables (coroutines, Tasks, Futures), explains await yields control to the event loop until completion, and warns that a bare coroutine call does not run it.
What is the difference between async def and regular functions?
This tests async def call semantics. A strong answer contrasts regular functions, which execute immediately, with async def, which returns a coroutine object that does nothing until awaited or wrapped in create_task.
How does Uvicorn use asyncio to handle thousands of concurrent connections?
Tests async concurrency and the GIL. Great answers cover the event loop suspending coroutines at await, Uvicorn interleaving connections, and multi-process workers for parallelism. Red flag: claiming asyncio uses threads per request or bypasses the GIL.
How do you structure concurrent API calls with asyncio.gather in FastAPI?
Tests FastAPI async concurrency. Strong answer: async def endpoint with two async HTTP requests in asyncio.gather, cutting total latency from sum to max of the two. Red flag: using sync clients or threads instead of async I/O.
ARQ for FastAPI: Async Background Tasks
ARQ lets your FastAPI app offload heavy work to background workers, keeping the API responsive. It's a task queue built for asyncio. Use it for slow tasks like sending emails or processing data. The footgun is using blocking task libraries with async code.
asyncio Streams: High-Level Async Network I/O
asyncio Streams are like async file handles for the network. You get a reader/writer pair to await data, simplifying TCP clients and servers for basic protocols. The footgun: the default buffer limit is small; reading large data will fail unexpectedly.
Testing Async FastAPI with pytest-asyncio
To test async code, your tests must also be async. `pytest-asyncio` lets you write `async def test_...` functions to `await` operations like database checks after an API call.
Motor: Don't Block Your Python App on MongoDB
Motor is the async bridge for Python apps to talk to MongoDB without blocking. Use it in FastAPI or other async frameworks to keep your server responsive during database queries.
SQLAlchemy 2.0: Async Without Blocking the Event Loop
SQLAlchemy 2.0 wraps its synchronous core with an async API, letting you `await` database calls without blocking your app's event loop. Use it in frameworks like FastAPI.
Debugging Python's Asyncio
Debugging asyncio is about finding what's blocking the single-threaded event loop. Use its debug mode to detect slow callbacks and `run_in_executor` to offload CPU-bound work. The biggest mistake is calling blocking code directly, which stalls the entire app.
asyncio Event Loop Policies: A Deprecated Pattern
Think of an event loop policy as the global factory for asyncio's event loops, controlling which loop is created and how it's retrieved. It was used to swap implementations, but the entire API is deprecated in Python 3.14 and will be removed in 3.16.
asyncio: Transports Move Bytes, Protocols Decide Which Bytes
asyncio Transports are the "how" (moving bytes), while Protocols are the "what" (deciding which bytes to send). They're the low-level foundation for libraries handling raw socket I/O.
Async Generators: `yield` in an `async` World
Async generators let you write I/O-bound data streams with the elegance of `yield`. An `async def` function with `yield` produces values one at a time, pausing for I/O without blocking. This is ideal for streaming data from a database.
Python's Asyncio Subprocesses: Non-Blocking Shell Commands
Run external commands without blocking your async app's event loop. `asyncio.create_subprocess_shell` lets you launch processes and await their results, keeping your server responsive.
asyncio Queues: Coordinating Asynchronous Tasks
An asyncio queue is a channel for coroutines to safely exchange data. It's ideal for producer-consumer patterns, like a web crawler feeding URLs to parsers. The main footgun: it's not thread-safe and must be used within a single event loop.
Get Asyncio bites daily.
Five a day, five minutes, offline. With quizzes so it sticks.
Open testing — you’ll join as an early tester.