Dmitrii Korolkov

github | email

Using Asyncio in Python

Asynchronous programming in Python allows you to write code that doesn’t block while waiting for external operations like network calls or disk I/O. Using async and await, you can speed up your applications without using threads or complex concurrency models.


1. What Is Async in Python?


# Normal (blocking) example
import time

def task():
    time.sleep(1)
    print("Done")

task()
print("All tasks done.")
                        

The above code waits before printing anything. With async, we can avoid this blocking behavior.


2. Basic Async/Await Syntax


import asyncio

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

asyncio.run(say_hello())
                        

3. Running Multiple Coroutines


import asyncio

async def greet(name):
    print(f"Hi {name}")
    await asyncio.sleep(1)
    print(f"Bye {name}")

async def main():
    await asyncio.gather(
        greet("Alice"),
        greet("Bob"),
        greet("Charlie")
    )

asyncio.run(main())
                        

4. Using Async with HTTP Requests (aiohttp)


import asyncio
import aiohttp

async def fetch(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            print(f"{url} - {response.status}")

urls = ["https://example.com", "https://python.org", "https://httpbin.org/get"]

async def main():
    tasks = [fetch(url) for url in urls]
    await asyncio.gather(*tasks)

asyncio.run(main())
                        

5. Creating a Simple Async Test


import asyncio
import unittest

class AsyncTest(unittest.IsolatedAsyncioTestCase):
    async def test_async_function(self):
        async def slow_add(a, b):
            await asyncio.sleep(1)
            return a + b

        result = await slow_add(3, 4)
        self.assertEqual(result, 7)

if __name__ == "__main__":
    unittest.main()
                        

6. Custom Sleep Loops


import asyncio

async def count():
    for i in range(5):
        print(f"Count {i}")
        await asyncio.sleep(0.5)

asyncio.run(count())
                        

7. Handling Exceptions in Async


import asyncio

async def risky_task():
    raise ValueError("Oops!")

async def main():
    try:
        await risky_task()
    except ValueError as e:
        print(f"Caught: {e}")

asyncio.run(main())
                        

8. Writing Async Context Managers


class AsyncResource:
    async def __aenter__(self):
        print("Starting")
        return self

    async def __aexit__(self, exc_type, exc, tb):
        print("Cleaning up")

async def use_resource():
    async with AsyncResource():
        print("Using resource")

asyncio.run(use_resource())
                        

9. Async Tips


# Only use await inside async def
# Don't block the event loop with time.sleep()
# Use asyncio.sleep() instead
# For CPU-bound tasks, consider multiprocessing instead of asyncio
                        

10. When Not to Use Async


# Async is great for:
# - I/O-bound operations
# - Network tasks
# - Waiting for data

# Async is NOT ideal for:
# - Heavy CPU tasks
# - Simple scripts that don’t wait for anything