Skip to content

Asyncio

Developer UtilitiesCLI/UtilsPython

What it is

Asyncio is Python's standard library for writing concurrent code using the async/await syntax. It provides an event loop, coroutines, tasks, and futures to facilitate asynchronous programming and I/O-bound operations.

Asyncio allows you to define coroutines with `async def`, schedule them for execution, and manage asynchronous tasks. It is ideal for network operations, file I/O, concurrency, and writing scalable applications.

Installation

Included in Python standard library (Python 3.4+)

Getting started

The smallest useful thing you can do with it, and what each part means.

Simple coroutine
import asyncio

async def say_hello():
    print('Hello')
    await asyncio.sleep(1)
    print('World')

asyncio.run(say_hello())
Defines a coroutine using `async def` and runs it with `asyncio.run()`. Demonstrates non-blocking sleep.
Running multiple coroutines concurrently
import asyncio

async def task(name, delay):
    await asyncio.sleep(delay)
    print(f'Task {name} completed')

async def main():
    await asyncio.gather(task('A', 2), task('B', 1))

asyncio.run(main())
Uses `asyncio.gather` to run multiple coroutines concurrently, printing output when each finishes.

Advanced usage

Where the library earns its place over a simpler alternative.

Creating tasks
import asyncio

async def my_task():
    await asyncio.sleep(1)
    print('Task done')

task = asyncio.create_task(my_task())
asyncio.run(task)
Wraps a coroutine in a Task, allowing it to run in the background.
Using async context managers
import asyncio

class AsyncResource:
    async def __aenter__(self):
        print('Enter')
        return self
    async def __aexit__(self, exc_type, exc, tb):
        print('Exit')

async def main():
    async with AsyncResource():
        print('Inside context')

asyncio.run(main())
Demonstrates the use of async context managers to manage resources asynchronously.
Networking with asyncio
import asyncio

async def tcp_echo_client(message):
    reader, writer = await asyncio.open_connection('127.0.0.1', 8888)
    writer.write(message.encode())
    await writer.drain()
    data = await reader.read(100)
    print(f'Received: {data.decode()}')
    writer.close()
    await writer.wait_closed()

asyncio.run(tcp_echo_client('Hello'))
Shows a simple TCP client using asyncio for asynchronous network I/O.
Scheduling periodic tasks
import asyncio

async def periodic():
    while True:
        print('Tick')
        await asyncio.sleep(1)

asyncio.run(periodic())
Runs a coroutine repeatedly at fixed intervals using a loop with `await asyncio.sleep()`.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

RuntimeError: Event loop is closed
Ensure you are not calling `asyncio.run()` from within an already running event loop.
CancelledError
Occurs when a task is cancelled. Handle with try/except around awaited tasks.
TimeoutError
Use `asyncio.wait_for()` or `asyncio.timeout()` to limit the duration of coroutines safely.

Best practices

  • Prefer `asyncio.run()` for top-level entry points in Python 3.7+.
  • Use `async/await` syntax instead of `@asyncio.coroutine` for readability.
  • Leverage `asyncio.gather()` for running multiple coroutines concurrently.
  • Use Tasks for background execution and long-running operations.
  • Avoid blocking calls in async functions; use non-blocking I/O libraries.

Background

Why it exists, and what it was reacting to.

Asyncio was introduced in Python 3.4 (2014) to provide a built-in framework for asynchronous programming, inspired by frameworks like Twisted and Tornado. It enables efficient handling of I/O-bound tasks, networking, and high-performance applications without using traditional threading or multiprocessing.