Skip to the content.

asyncio — cooperative async I/O

import asyncio

A single-threaded event loop, modelled on CPython’s asyncio. Useful for managing multiple concurrent socket connections without threads.

Basics

Define a coroutine

async def greet(name):
    await asyncio.sleep(0.5)
    print('hello', name)

Run a coroutine

asyncio.run(greet('World'))
# (waits 500 ms, then prints 'hello World')

Run several together — asyncio.gather

async def main():
    await asyncio.gather(
        greet('A'),
        greet('B'),
        greet('C'),
    )

asyncio.run(main())
# Three greetings appear roughly together after 500 ms (not 1500 ms).

Run with a timeout — asyncio.wait_for

async def slow():
    await asyncio.sleep(10)
    return 'done'

try:
    result = await asyncio.wait_for(slow(), 1.0)
except asyncio.TimeoutError:
    print('timed out')

Streams (TCP)

asyncio.open_connection(host, port) returns (reader, writer):

async def fetch_motd():
    r, w = await asyncio.open_connection('10.0.2.2', 17)   # qotd
    line = await r.readline()
    print('motd:', line)
    w.close()
    await w.wait_closed()

asyncio.run(fetch_motd())

StreamReader: .read(n), .readline(), .readexactly(n), .readuntil(sep). StreamWriter: .write(data), .drain(), .close(), .wait_closed(), .get_extra_info(name).

TCP server

async def handle(reader, writer):
    data = await reader.read(100)
    writer.write(data.upper())
    await writer.drain()
    writer.close()

asyncio.run(asyncio.start_server(handle, '0.0.0.0', 1234))

Sleep + sync primitives

Tasks

async def background():
    while True:
        await asyncio.sleep(1)
        print('tick')

async def main():
    task = asyncio.create_task(background())   # runs concurrently
    await asyncio.sleep(5)
    task.cancel()                              # raises CancelledError in it
    try:
        await task
    except asyncio.CancelledError:
        pass

asyncio.run(main())

Watch out


Credit: shape from MicroPython asyncio docs (MIT) and CPython asyncio tutorial (PSF).