Async Programming: asyncio, async/await

আধুনিক Python async — event loop ও cooperative concurrency

Read: ~35 min Hard 5 practice problems Live code runner

1. Why Async?

Most real-world programs spend more time waiting than computing — waiting for a web response, a database query, a file. asyncio lets you write cooperative code that, while one task waits, switches to another — all in a single thread with zero locks.

বেশিরভাগ বাস্তব প্রোগ্রাম গণনার চেয়ে অপেক্ষা-তে বেশি সময় কাটায় — web response, database query, file। asyncio এমন cooperative কোড লিখতে দেয় যেখানে একটি task অপেক্ষায় থাকলে অন্য একটি চলবে — সব একই thread-এ, কোনো lock ছাড়াই।

2. The Event Loop and Coroutines

The event loop is a scheduler that runs coroutines — special functions defined with async def. A coroutine pauses at every await statement; the loop then runs another coroutine until its awaited operation is ready.

event loop একটি scheduler যা coroutine-গুলো চালায় — async def দিয়ে ঘোষিত বিশেষ ফাংশন। প্রতিটি await-এ coroutine বিরতি নেয়, loop তখন অন্য একটি চালাতে শুরু করে।
asyncio Event Loop Event Loop async def fetch(): await http.get() async def read(): await f.read() await asyncio.sleep(1) Figure 33.1 — Event loop coroutine-দের schedule করে; প্রতিটি await-এ সুইচ হতে পারে।

3. Your First Async Program

Always start an async program with asyncio.run(main()) — that single call creates the event loop.

Async প্রোগ্রাম সব সময় asyncio.run(main()) দিয়ে শুরু করতে হবে — এই এক কল-ই event loop তৈরি করে।
hello_async.py
import asyncio

async def greet(name, delay):
    await asyncio.sleep(delay)
    print(f"Hello, {name}!")

async def main():
    await asyncio.gather(
        greet("Arif",  1),
        greet("Nabila", 2),
        greet("Rahim",  1),
    )

asyncio.run(main())

4. gather vs TaskGroup

asyncio.gather runs coroutines concurrently and returns their results in order. Python 3.11+ introduced TaskGroup, which automatically cancels siblings when one task raises — the preferred modern pattern.

asyncio.gather coroutine-গুলো একসাথে চালায় ও ফলাফল ফেরত দেয়। Python 3.11+ থেকে TaskGroup — এতে একটি task ব্যর্থ হলে অন্যগুলো স্বয়ংক্রিয়ভাবে cancel হয় — এটিই আধুনিক প্যাটার্ন।
taskgroup.py
import asyncio

async def work(i):
    await asyncio.sleep(0.5)
    return i * i

async def main():
    async with asyncio.TaskGroup() as tg:
        tasks = [tg.create_task(work(i)) for i in range(1, 6)]
    print([t.result() for t in tasks])

asyncio.run(main())

5. Timeouts and Cancellation

asyncio.wait_for gives a coroutine a deadline; if exceeded it raises TimeoutError and cleanly cancels the task.

asyncio.wait_for coroutine-কে একটি deadline দেয় — সময় পেরিয়ে গেলে TimeoutError raise করে এবং task clean ভাবে cancel হয়।
timeout.py
import asyncio

async def slow():
    await asyncio.sleep(3)
    return "done"

async def main():
    try:
        result = await asyncio.wait_for(slow(), timeout=1.0)
        print(result)
    except asyncio.TimeoutError:
        print("timed out!")

asyncio.run(main())

6. Common Pitfalls

⚠️ Don't do this

  • Calling blocking time.sleep inside a coroutine
  • Using requests in async code (it is blocking)
  • CPU-heavy work without run_in_executor
  • Forgetting to await a coroutine

✅ Do this instead

  • await asyncio.sleep(seconds)
  • httpx.AsyncClient or aiohttp
  • await loop.run_in_executor(None, heavy_fn)
  • Always await, or store in a Task

7. Vocabulary (শব্দভাণ্ডার)

TermMeaningবাংলায়
CoroutineFunction defined with async def, can pause at await.async def-এ সংজ্ঞায়িত ফাংশন, await-এ বিরতি নিতে পারে।
Event loopScheduler that runs and resumes coroutines.Coroutine চালানো ও resume করার scheduler।
TaskA coroutine scheduled to run in the loop.Loop-এ চালানোর জন্য scheduled coroutine।
AwaitableAnything that can be used with await.await-এর সাথে ব্যবহারযোগ্য।
gatherRun several coroutines concurrently.একসাথে কয়েকটি coroutine চালানো।
TaskGroup3.11+ structured-concurrency context manager.3.11+ structured-concurrency context manager।

8. Practice Problems

  1. Write a coroutine that sleeps 1 second and returns the string "ready"; run it with asyncio.run.
    একটি coroutine লিখুন যা ১ সেকেন্ড sleep করবে এবং "ready" return করবে; asyncio.run-এ চালান।
    ✨ Show Answer (উত্তর দেখুন)
    ans1.py
    import asyncio
    async def ready():
        await asyncio.sleep(1)
        return "ready"
    print(asyncio.run(ready()))
  2. Run 5 coroutines concurrently with asyncio.gather; each returns its index squared.
    ৫টি coroutine asyncio.gather-এ একসাথে চালান; প্রতিটি তার index-এর square ফেরত দেবে।
    ✨ Show Answer (উত্তর দেখুন)
    ans2.py
    import asyncio
    async def sq(i):
        await asyncio.sleep(0.2)
        return i*i
    async def main():
        print(await asyncio.gather(*[sq(i) for i in range(5)]))
    asyncio.run(main())
  3. Wrap a slow coroutine with a 1-second timeout and handle TimeoutError.
    ধীর coroutine-কে ১ সেকেন্ডের timeout দিন ও TimeoutError handle করুন।
    ✨ Show Answer (উত্তর দেখুন)
    ans3.py
    import asyncio
    async def slow():
        await asyncio.sleep(3)
    async def main():
        try:
            await asyncio.wait_for(slow(), 1)
        except asyncio.TimeoutError:
            print("gave up")
    asyncio.run(main())
  4. What does await actually do? Explain in two sentences.
    await আসলে কী করে — দুই বাক্যে বলুন।
    ✨ Show Answer (উত্তর দেখুন)

    Answer: await suspends the current coroutine and hands control back to the event loop, which can schedule another coroutine in the meantime. When the awaited awaitable is ready, the loop resumes the coroutine with its result.

    await বর্তমান coroutine-কে বিরতি দিয়ে control event loop-কে ফিরিয়ে দেয়; loop তখন অন্য coroutine চালাতে পারে। awaitable প্রস্তুত হলে loop ফলাফল সহ coroutine-কে resume করে।

  5. Use a TaskGroup to run 3 concurrent tasks and collect their results.
    একটি TaskGroup দিয়ে ৩টি task একসাথে চালিয়ে ফলাফল সংগ্রহ করুন।
    ✨ Show Answer (উত্তর দেখুন)
    ans5.py
    import asyncio
    async def w(x):
        await asyncio.sleep(0.2)
        return x * 10
    async def main():
        async with asyncio.TaskGroup() as tg:
            a = tg.create_task(w(1))
            b = tg.create_task(w(2))
            c = tg.create_task(w(3))
        print(a.result(), b.result(), c.result())
    asyncio.run(main())

Summary — Module 33

asyncio provides single-threaded cooperative concurrency through async def coroutines and the await keyword. Use asyncio.run to start, gather or TaskGroup to run many tasks, and wait_for for timeouts. Never block the loop with time.sleep or blocking libraries.

asyncio single-thread-এ cooperative concurrency দেয় — async def ও await। asyncio.run দিয়ে শুরু, gather/TaskGroup দিয়ে অনেক task, wait_for দিয়ে timeout। Loop কখনো time.sleep বা blocking লাইব্রেরি দিয়ে আটকাবেন না।

Next Module → Metaprogramming: Metaclasses ও Descriptors।