Python3.x Python asyncio Module
\\n\\nasyncio is a module in the Python standard library for writing asynchronous I/O operations code.
asyncio provides an efficient way to handle concurrent tasks, especially suitable for I/O-intensive operations such as network requests, file read/write, etc.
\\n\\nBy using asyncio, you can handle multiple tasks simultaneously in a single thread without using multithreading or multiprocessing.
Why do we need asyncio?
\\n\\nIn traditional synchronous programming, when a task needs to wait for an I/O operation (such as a network request) to complete, the program blocks until the operation is finished. This leads to low program efficiency, especially when dealing with a large number of I/O operations.
\\n\\nasyncio improves program concurrency and efficiency by introducing an asynchronous programming model that allows the program to continue executing other tasks while waiting for I/O operations.
\\n\\n\\nImagine you are running a restaurant:
\\n\\n
\\n- Synchronous mode (ordinary function): You have only one chef. Guest A orders a steak, and the chef starts cooking it (which takes 5 minutes). During these 5 minutes of cooking, the chef is completely occupied and cannot do anything else. Even if Guest B just wants a glass of water, they must wait idly.
\\n- Asynchronous mode (asyncio): You have multiple chefs (actually still one, but very smart). After the chef starts cooking Guest A's steak, realizing it needs to wait, they immediately mark that steak as pending, then turn to pour water for Guest B. After pouring the water, they check if the steak is almost ready; if not, they can go handle Guest C's order. This way, during the waiting time for I/O (such as cooking steak, network requests, reading/writing files), the chef (CPU) is constantly working efficiently.
\\nasyncio is the standard library Python uses to implement this smart working pattern. It allows you to write single-threaded concurrent code, especially suitable for I/O-intensive scenarios such as web crawlers, web servers, and microservices.
\\n
Its core components are the event loop, coroutines, and tasks.
\\n\\n\\n\\n
1. Coroutine
\\n\\nCoroutine is one of the core concepts of asyncio. It is a special function that can pause during execution and resume later. Coroutines are defined with the async def keyword and paused with the await keyword, waiting for asynchronous operations to complete.
Example
\\n\\nimport asyncio\\n\\nasync def say_hello():\\n print("Hello")\\n await asyncio.sleep(1)\\n print("World")\\n\\n\\n2. Event Loop
\\n\\nThe event loop is the core component of asyncio, responsible for scheduling and executing coroutines. It continuously checks if there are tasks to execute and calls the corresponding callback functions after tasks are completed.
Example
\\n\\nasync def main():\\n await say_hello()\\n\\nasyncio.run(main())\\n\\n\\n3. Task
\\n\\nA task is a wrapper for a coroutine, representing a coroutine that is currently executing or will be executed. You can create tasks through the asyncio.create_task() function and add them to the event loop.
Example
\\n\\nasync def main():\\n task = asyncio.create_task(say_hello())\\n await task\\n\\n\\n4. Future
\\n\\nFuture is an object representing the result of an asynchronous operation. It is typically used in low-level APIs to represent an operation that has not yet completed. You can wait for a Future to complete using the await keyword.
Example
\\n\\nasync def main():\\n future = asyncio.Future()\\n await future\\n\\n\\nBasic Usage and Code Examples
\\n\\nLet's understand the above concepts through a classic example of concurrently accessing multiple URLs.
\\n\\nSuppose we need to fetch content from three different URLs. Using synchronous methods would execute them sequentially, with total time being the sum of the three request durations. Using asyncio, we can send these three requests simultaneously, with total time close to the slowest request.
Synchronous Version (for comparison)
\\n\\nExample
\\n\\nimport time\\nimport requests\\n\\ndef fetch_url(url):\\n """Simulate a time-consuming network request (synchronous version)"""\\n print(f"Start fetching: {url}")\\n time.sleep(2) # Simulate 2 seconds network delay\\n print(f"Finish fetching: {url}")\\n return f"From {url} Data"\\n\\ndef main_sync():\\n urls = ['https://example.com/1', 'https://example.com/2', 'https://example.com/3']\\n results = []\\n \\n start = time.time()\\n \\n for url in urls:\\n result = fetch_url(url) # Must wait for the previous one to complete before starting the next\\n results.append(result)\\n \\n end = time.time()\\n \\n print(f"Synchronous version total time taken: {end - start:.2f} Seconds")\\n print(f"Result: {results}")\\n\\nif __name__ == "__main__":\\n main_sync()\\n\\n\\nExpected Output:
\\n\\nStart fetching: https://example.com/1\\nFinish fetching: https://example.com/1\\nStart fetching: https://example.com/2\\nFinish fetching: https://example.com/2\\nStart fetching: https://example.com/3\\nFinish fetching: https://example.com/3\\nSynchronous version total time taken: 6.00 Seconds\\nResult: ['From https://example.com/1 Data', 'From https://example.com/2 Data', 'From https://example.com/2 Data']\\n\\nTotal time spent is approximately 6 seconds.
\\n\\nAsynchronous Version (using asyncio)
\\n\\nWe need to use the aiohttp library instead of requests for asynchronous HTTP requests. First install it: pip install aiohttp.
Example
\\n\\nimport asyncio\\nimport aiohttp\\nimport time\\n\\nasync def fetch_url_async(session, url):\\n """Simulate a time-consuming network request (asynchronous version)"""\\n print(f"Start asynchronous fetching: {url}")\\n # Note: Here we use aiohttp's asynchronous get method, and await it\\n \\n async with session.get(url) as response:\\n # Simulate processing response also takes time\\n await asyncio.sleep(2) # Use asyncio.sleep to simulate I/O waiting, it does not block the thread\\n text = await response.text()\\n \\n print(f"Finish asynchronous fetching: {url}")\\n return f"From {url} Data (Length: {len(text)})"\\n\\nasync def main_async():\\n urls = ['https://httpbin.org/get', 'https://httpbin.org/delay/1', 'https://httpbin.org/headers']\\n \\n async with aiohttp.ClientSession() as session: # Create asynchronous HTTP session\\n # Create a task for each URL\\n tasks = []\\n for url in urls:\\n # create_task adds the coroutine to the event loop and immediately starts scheduling\\n task = asyncio.create_task(fetch_url_async(session, url))\\n tasks.append(task)\\n \\n print("All tasks have been created, starting concurrent execution...")\\n \\n # Use asyncio.gather to run all tasks concurrently and wait for them all to complete\\n # gather returns a list of results, in the same order as the tasks passed in\\n results = await asyncio.gather(*tasks)\\n \\n return results\\n\\nif __name__ == "__main__":\\n start = time.time()\\n # asyncio.run() is a convenient way to start the event loop and run the top-level coroutine\\n final_results = asyncio.run(main_async())\\n end = time.time()\\n \\n print(f"nAsynchronous version total time taken: {end - start:.2f} Seconds")\\n for res in final_results:\\n print(res)\\n\\n\\nExpected Output:
\\n\\nAll tasks have been created, starting concurrent execution...\\nStart asynchronous fetching: https://httpbin.org/get\\nStart asynchronous fetching: https://httpbin.org/delay/1\\nStart asynchronous fetching: https://httpbin.org/headers\\nοΌAbout 2 SecondsPost, all requests complete almost simultaneously)\\nFinish asynchronous fetching: https://httpbin.org/headers\\nFinish asynchronous fetching: https://httpbin.org/get\\nFinish asynchronous fetching: https://httpbin.org/delay/1\\n\\nAsynchronous version total time taken: 2.10 Seconds # Note! The total elapsed time is far less than 6 seconds\\nFrom https://httpbin.org/get Data (Length: 274)\\nFrom https://httpbin.org/delay/1 Data (Length: 392)\\nFrom https://httpbin.org/headers Data (Length: 177)\\n\\n\\nCode Analysis:
\\n\\n- \\n
async def: Defines the coroutine functionsfetch_url_asyncandmain_async. \\nawait: Infetch_url_async, weawait session.get()andawait response.text(), which tells the event loop: "This network request takes time, go ahead and execute other ready tasks." \\nasyncio.create_task(): Wraps thefetch_url_asynccoroutine into aTask, allowing it to be scheduled by the event loop to achieve concurrency. \\nasyncio.gather(*tasks): A very practical function that runs all passed coroutines/tasks concurrently, waits for them all to complete, and finally collects all results. \\nasyncio.run(main_async()): The recommended way in Python 3.7+, it is responsible for creating the event loop, running the coroutine, and closing the loop. \\n
Key Functions and Parameters Explanation
\\n\\nBelow are the most commonly used high-level functions in asyncio in table form:
| Function | \\nMain Purpose | \\nCommon Parameters | \\n
|---|---|---|
asyncio.run(coro, *, debug=False) | \\nRun a top-level coroutine, managing the lifecycle of the event loop. This is the main entry point of the program. | \\ncoro: The coroutine object to run. debug: Set to True to enable debug mode for the event loop. | \\n
asyncio.create_task(coro, *, name=None) | \\nWrap a coroutine into a Task object and schedule it in the event loop. This is the main way to achieve concurrency. | \\ncoro: The coroutine object to wrap. name: (Python 3.8+) Assign a name to the task for easier debugging. | \\n
asyncio.gather(*aws, return_exceptions=False) | \\nRun multiple asynchronous tasks concurrently (aws can accept coroutines, tasks, etc.), wait for all to complete, and return a list of results. | \\n*aws: Variable arguments, pass multiple asynchronous objects. return_exceptions: Defaults to False; if any task raises an exception, it will immediately propagate to the caller of gather. When set to True, exceptions will be returned as part of the results. | \\n
asyncio.sleep(delay, result=None) | \\nAsynchronously sleep for the specified number of seconds. This is the key difference from time.sleep (blocking). | \\ndelay: Number of seconds to sleep. result: Value to return after sleep completes. | \\n
asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED) | \\nRun tasks concurrently and wait until the specified condition is met. Returns two sets (done, pending), which are completed and pending tasks respectively. | \\naws: Collection of asynchronous objects. timeout: Timeout in seconds. return_when: Determines when to return, options: FIRST_COMPLETED (first completed), FIRST_EXCEPTION (first exception), ALL_COMPLETED (all completed, default). | \\n
asyncio.to_thread(func, /, *args, **kwargs) | \\n(Python 3.9+) Run an ordinary, potentially blocking synchronous function in a separate thread and return an awaitable coroutine. Used for handling CPU-intensive or blocking I/O. | \\nfunc: The synchronous function to run in the thread. *args, **kwargs: Arguments passed to the function. | \\n
Visual Understanding: Asynchronous Task Scheduling Flow
\\n\\nDiagram Explanation: This flowchart shows how the event loop works like a dispatcher. It maintains a task queue. When a task reaches await (e.g., waiting for a network response), it is suspended, and the event loop immediately finds the next runnable (ready) task from the queue to execute. When the I/O operation of the suspended task completes, the event loop receives notification, changes that task's status back to ready, and continues executing it at some point in the future. Through this method, during I/O waiting periods, the CPU is fully utilized to execute other tasks, achieving concurrency within a single thread.
\\n\\n
asyncio Basic Usage
\\n\\n1. Running Coroutines
\\n\\nTo run a coroutine, you can use the asyncio.run() function. It creates an event loop and runs the specified coroutine.
Example
\\n\\nimport asyncio\\n\\nasync def main():\\n print("Start")\\n await asyncio.sleep(1)\\n print("End")\\n\\nasyncio.run(main())\\n\\n\\n2. Concurrently Executing Multiple Tasks
\\n\\nYou can use the asyncio.gather() function to concurrently execute multiple coroutines and wait for them all to complete.
Example
\\n\\nimport asyncio\\n\\nasync def task1():\\n print("Task 1 started")\\n await asyncio.sleep(1)\\n print("Task 1 finished")\\n\\nasync def task2():\\n print("Task 2 started")\\n await asyncio.sleep(2)\\n print("Task 2 finished")\\n\\nasync def main():\\n await asyncio.gather(task1(), task2())\\n\\nasyncio.run(main())\\n\\n\\n3. Timeout Control
\\n\\nYou can use the asyncio.wait_for() function to set a timeout for a coroutine. If the coroutine does not complete within the specified time, an asyncio.TimeoutError exception will be raised.
Example
\\n\\nimport asyncio\\n\\nasync def long_task():\\n await asyncio.sleep(10)\\n print("Task finished")\\n\\nasync def main():\\n try:\\n await asyncio.wait_for(long_task(), timeout=5)\\n except asyncio.TimeoutError:\\n print("Task timed out")\\n\\nasyncio.run(main())\\n\\n\\n\\n\\n
asyncio Application Scenarios
\\n\\nasyncio is particularly suitable for the following scenarios:
- \\n
- Network Requests: Such as HTTP requests, WebSocket communication, etc. \\n
- File I/O: Such as asynchronous file reading/writing. \\n
- Database Operations: Such as asynchronous database access. \\n
- Real-time Data Processing: Such as real-time message queue processing. \\n
\\n\\n
Common Classes, Methods, and Functions
\\n\\n1. Core Functions
\\n\\n| Method/Function | \\nDescription | \\nExample | \\n
|---|---|---|
asyncio.run(coro) | \\nRun asynchronous main function (Python 3.7+) | \\nasyncio.run(main()) | \\n
asyncio.create_task(coro) | \\nCreate task and add to event loop | \\ntask = asyncio.create_task(fetch_data()) | \\n
asyncio.gather(*coros) | \\nRun multiple coroutines concurrently | \\nawait asyncio.gather(task1, task2) | \\n
asyncio.sleep(delay) | \\nAsynchronous wait (non-blocking) | \\nawait asyncio.sleep(1) | \\n
asyncio.wait(coros) | \\nControl task completion method | \\ndone, pending = await asyncio.wait([task1, task2]) | \\n
2. Event Loop
\\n\\n| Method | \\nDescription | \\nExample | \\n
|---|---|---|
loop.run_until_complete(future) | \\nRun until task completes | \\nloop.run_until_complete(main()) | \\n
loop.run_forever() | \\nRun event loop forever | \\nloop.run_forever() | \\n
|
YouTip