Backend Development 7 min read

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.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Understanding Python Coroutines and Asynchronous HTTP Requests with httpx

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.

backendPerformanceasyncCoroutinehttpx
Python Programming Learning Circle
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.