The asyncio library in Python provides a framework for writing single-threaded concurrent code using coroutines, which are a type of asynchronous function. It allows you to manage asynchronous operations easily and is suitable for I/O-bound and high-level structured network code.
Key Concepts
- Event Loop: The core of every
asyncio
application. It runs asynchronous tasks and callbacks, performs network I/O operations, and runs subprocesses. - Coroutines: Special functions defined with
async def
, which can useawait
to call other asynchronous functions. - Tasks: A higher-level way to manage coroutines. They allow you to schedule coroutines concurrently.
- Futures: Objects that represent the result of an asynchronous operation, typically managed by the event loop.
- Streams: High-level APIs for working with network connections.
Basic Usage
Here’s a simple example of using asyncio
to run a couple of coroutines:
import asyncio
async def say_hello():
await asyncio.sleep(1)
print("Hello")
async def say_world():
await asyncio.sleep(1)
print("World")
async def main():
await asyncio.gather(
say_hello(),
say_world(),
)
asyncio.run(main())
Creating Tasks
You can use asyncio.create_task()
to schedule a coroutine to run concurrently:
async def task_example():
print("Task started")
await asyncio.sleep(2)
print("Task finished")
async def main():
task = asyncio.create_task(task_example())
await task
asyncio.run(main())
Anticipate Futures
Futures represent a value that may not be available yet. You can create and wait for a future:
import asyncio
async def set_future(fut):
await asyncio.sleep(2)
fut.set_result("Future is done")
async def main():
fut = asyncio.Future()
await asyncio.gather(set_future(fut))
print(fut.result())
asyncio.run(main())
streams
Working with TCP streams using asyncio
is straightforward:
import asyncio
async def handle_client(reader, writer):
data = await reader.read(100)
message = data.decode()
addr = writer.get_extra_info(‘peername’)
print(f"Received {message} from {addr}")
writer.write(data)
await writer.drain()
writer.close()
async def main():
server = await asyncio.start_server(handle_client, ‘127.0.0.1’, 8888)
addr = server.sockets[0].getsockname()
print(f’Serving on {addr}’)
async with server:
await server.serve_forever()
asyncio.run(main())
Exception Handling
You can handle exceptions within asyncio
tasks:
async def error_task():
raise ValueError("An example error")
async def main():
task = asyncio.create_task(error_task())
try:
await task
except ValueError as e:
print(f"Caught an exception: {e}")
asyncio.run(main())
Integration with Other Libraries
asyncio
can be integrated with various libraries, including web frameworks like FastAPI
, databases, and more. This allows for highly responsive applications that can handle many tasks concurrently.
Useful Functions
asyncio.sleep()
: Sleep for a given number of seconds.asyncio.gather()
: Run multiple coroutines concurrently and wait for them to complete.asyncio.wait_for()
: Wait for a coroutine with a timeout.asyncio.shield()
: Protect a task from cancellation.asyncio.run()
: Run an event loop until the given coroutine completes.