Source: Asynchronous Programming in Python

Concurrent vs Parallel

  • Concurrent: tasks share one thread, taking turns — NOT running at the same time
  • Parallel: tasks run simultaneously on multiple CPU cores (requires multiprocessing)
  • asyncio is concurrent, not parallel

Synchronous vs Async

Synchronous (normal code): each task waits for the previous to finish. Bottleneck: idle waiting (HTTP calls, file I/O, sleep, database calls).

Asynchronous (asyncio): while one task waits, the next task runs. Total CPU work is the same — less idle time.


asyncio API

Core rule

All awaitable objects must run inside an event loop. asyncio.run() creates the top-level event loop.

Coroutines

Declared with async def. Called with await or asyncio.run().

import asyncio
 
async def countdown(name, seconds):
    for i in range(seconds, 0, -1):
        print(name, i)
        await asyncio.sleep(1)   # yields to event loop
    print("done!")
 
async def main():
    await countdown("simple", 4)
 
asyncio.run(main())
# Output: simple 4 / simple 3 / simple 2 / simple 1 / done!

Tasks (concurrent execution)

asyncio.create_task() schedules a coroutine to run concurrently. Must be awaited to ensure it completes.

async def main():
    taskA = asyncio.create_task(countdown("A", 3))
    taskB = asyncio.create_task(countdown("B", 2))
 
    await asyncio.sleep(1)
    print("Doing something else...")
 
    await taskA   # wait for A to finish
    await taskB   # wait for B to finish
 
asyncio.run(main())
# Output interleaved: A 3 / B 2 / Doing something else... / A 2 / B 1 / A 1 / done! / done!

gather() — run multiple coroutines and wait for all

async def main():
    await asyncio.gather(
        countdown("A", 4),
        countdown("B", 3)
    )
    print("Everything done!")
 
asyncio.run(main())

Futures

  • Low-level awaitable object (equivalent of a JavaScript Promise)
  • Rare to use directly — asyncio handles them internally

Awaitable Objects Summary

TypeDescription
Coroutineasync def function — awaited with await
TaskScheduled coroutine — created with asyncio.create_task()
FutureLow-level — rarely used directly

Key Pattern for IoT

Use asyncio to concurrently handle:

  • Reading from sensor
  • Publishing to MQTT
  • Listening for commands
async def read_sensor():
    while True:
        value = sensor.read()
        await asyncio.sleep(5)
 
async def publish_mqtt():
    while True:
        await client.publish(topic, payload)
        await asyncio.sleep(5)
 
asyncio.run(asyncio.gather(read_sensor(), publish_mqtt()))

See Also