💡 Advanced Python Concepts
Estimated reading: 4 minutes 269 views

Python Coroutines Explained – asyncio, await, gather Examples

Introduction – Why Use Coroutines in Python?

Modern Python applications—like web servers, real-time APIs, and event-driven systems—demand non-blocking, asynchronous behavior. Enter coroutines.

Coroutines in Python allow you to:

  • Pause and resume execution
  • Run concurrent tasks without threads
  • Build highly responsive, non-blocking applications

Coroutines are the foundation of Python’s async/await syntax introduced in Python 3.5+.

In this guide, you’ll learn:

  • What coroutines are and how they differ from generators
  • How to define and run async coroutines
  • Real-world examples using asyncio
  • Best practices and when to use coroutines over threads

What Is a Coroutine?

A coroutine is a special function declared with async def that can pause execution with await and resume later.

import asyncio

async def greet():
    print("Hello")
    await asyncio.sleep(1)
    print("World")

You can only await inside an async def function.


How to Run a Coroutine

async def main():
    await greet()

asyncio.run(main())

Output:

Hello
(wait 1 second)
World

asyncio.run() is the recommended way to start the event loop in Python 3.7+.


Coroutines vs Generators

FeatureGeneratorsCoroutines (async)
Keyworddef, yieldasync def, await
Use CaseLazy iterationAsynchronous programming
Pause Withyieldawait
SchedulerManual or loopsasyncio event loop

️ Example – Simulate Concurrent Tasks

import asyncio

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

async def main():
    await asyncio.gather(
        task("Task 1", 2),
        task("Task 2", 1)
    )

asyncio.run(main())

Output:

Task 1 started  
Task 2 started  
Task 2 completed  
Task 1 completed

Coroutines run concurrently but not in parallel (no threads involved).


Awaitable Objects

You can await on:

  • Coroutines (async def)
  • asyncio.sleep, asyncio.open_connection, etc.
  • Objects implementing __await__()

Create Custom Awaitables

class Wait:
    def __await__(self):
        yield from asyncio.sleep(1).__await__()

async def use_wait():
    print("Waiting...")
    await Wait()
    print("Done")

asyncio.run(use_wait())

You can define your own awaitable classes for advanced control.


Real-World Use Case – Async HTTP Requests

import aiohttp
import asyncio

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            return await response.text()

async def main():
    html = await fetch("https://example.com")
    print(html[:100])

asyncio.run(main())

No threads, no blocking—ideal for I/O-heavy workloads like web scrapers or APIs.


Coroutines Are Not Threads

FeatureCoroutineThread
Switch TimeExplicit (await)Preemptive (by OS)
MemoryLightweightHeavier
ParallelismNo (single-threaded)Yes (true parallel threads)
Best ForI/O-bound tasksCPU-bound or parallel tasks

Best Practices

Do This Avoid This
Use asyncio.run() to start your appNesting asyncio.run() inside coroutines
Use await for non-blocking I/OCalling blocking code inside coroutines
Use asyncio.gather() for concurrent tasksAwaiting tasks one-by-one unnecessarily
Use libraries like aiohttp, aiomysqlMixing async and sync I/O

Summary – Recap & Next Steps

Python coroutines enable non-blocking, asynchronous programming using modern syntax (async/await). They’re powerful tools for building scalable, I/O-bound applications.

Key Takeaways:

  • async def defines a coroutine
  • Use await to pause execution
  • Use asyncio.run() to launch coroutines
  • Coroutines are lightweight and ideal for I/O-bound tasks
  • Don’t use coroutines for CPU-bound tasks—use multiprocessing instead

Real-World Relevance:
Used in async web frameworks (FastAPI, Sanic), chatbots, IoT systems, web scrapers, and real-time APIs.


FAQ – Python Coroutines

What’s the difference between a coroutine and a regular function?

Coroutines are declared with async def and use await to pause and resume.

Can coroutines run in parallel?

No. Coroutines run concurrently on the same thread. Use threads or processes for true parallelism.

How do I call a coroutine?

Use await coroutine() inside another coroutine, or asyncio.run() at the top level.

Can I use await outside of a function?

No. await must be used inside an async def function.

Are coroutines faster than threads?

For I/O-bound tasks, yes. They use less memory and have lower overhead.


Share Now :
Share

Python Coroutines

Or Copy Link

CONTENTS
Scroll to Top