Understanding Python Coroutines and Asynchronous HTTP Requests with httpx
This article explains the concept of Python coroutines, compares them with multithreading, outlines suitable and unsuitable scenarios, introduces the httpx library for asynchronous HTTP calls, and demonstrates performance gains through synchronous and asynchronous code examples.
During a recent backend refactor, the company switched most logic to asynchronous coroutines using Python's async / await syntax, prompting a deeper exploration of coroutines.
A coroutine is a lightweight, user‑space thread managed by the programmer, offering finer control than OS‑managed threads.
Compared with multithreading, coroutines keep control in user space, reduce context‑switch overhead, use far less stack memory (≈1 KB vs 1 MB), and avoid the need for locks because they run in a single thread.
Coroutines are ideal for I/O‑bound, highly concurrent workloads but unsuitable for CPU‑intensive tasks.
The article then introduces httpx , an async‑capable HTTP client that mirrors the popular requests API while supporting async calls.
Installation is straightforward: pip install httpx To illustrate the performance benefit, a synchronous benchmark makes 200 GET requests to http://www.baidu.com using httpx.get, taking about 16.6 seconds.
Code snippet for the synchronous test:
import asyncio
import httpx
import threading
import time
def sync_main(url, sign):
response = httpx.get(url).status_code
print(f'sync_main: {threading.current_thread()}: {sign}: {response}')
sync_start = time.time()
[sync_main(url='http://www.baidu.com', sign=i) for i in range(200)]
sync_end = time.time()
print(sync_end - sync_start)The asynchronous benchmark creates an httpx.AsyncClient and runs 200 concurrent GET requests with await client.get, completing in roughly 4.5 seconds—a 73 % reduction.
Code snippet for the asynchronous test:
import asyncio
import httpx
import threading
import time
client = httpx.AsyncClient()
async def async_main(url, sign):
response = await client.get(url)
status_code = response.status_code
print(f'async_main: {threading.current_thread()}: {sign}: {status_code}')
loop = asyncio.get_event_loop()
tasks = [async_main(url='http://www.baidu.com', sign=i) for i in range(200)]
async_start = time.time()
loop.run_until_complete(asyncio.wait(tasks))
async_end = time.time()
loop.close()
print(async_end - async_start)The results show that while the order of async outputs appears random due to coroutine switching, the main thread never switches, confirming the single‑threaded nature of coroutines.
In conclusion, adopting coroutines and async HTTP dramatically speeds up I/O‑bound testing tasks and enhances overall technical proficiency.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Python Programming Learning Circle
A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
