Awaitable: Giải mã 'lời hứa' bất đồng bộ trong Python (dành cho Gen Z)
Python

Awaitable: Giải mã 'lời hứa' bất đồng bộ trong Python (dành cho Gen Z)

Author

Admin System

@root

Ngày xuất bản

20 Mar, 2026

Lượt xem

3 Lượt

"awaitable"

Chào các bạn Gen Z mê code, anh Creyt đây! Hôm nay chúng ta sẽ cùng "đào" một khái niệm nghe có vẻ phức tạp nhưng lại cực kỳ "cool ngầu" trong Python: awaitable. Nghe cái tên đã thấy có mùi "chờ đợi" rồi đúng không? Chính xác! Nó chính là chìa khóa để ứng dụng của các bạn không còn "đứng hình" khi phải làm nhiều việc cùng lúc.

1. awaitable là gì mà "hot" vậy?

Thôi bỏ qua mấy cái định nghĩa khô khan trên trường đi. Cứ tưởng tượng thế này: Bạn đang đứng xếp hàng ở quán trà sữa đông nghẹt. Bạn order một ly Trà Sữa Full Topping.

  • Cách cũ (blocking): Bạn order xong, rồi cứ đứng trơ ra đó, nhìn anh pha chế làm từng bước một, không làm gì khác được. Cả thế giới bên ngoài có sập cũng mặc kệ. Quán có 100 người thì 100 người đứng nhìn, quán tắc nghẽn!
  • Cách mới (async/await): Bạn order xong, anh pha chế đưa cho bạn một cái "phiếu chờ" (hay còn gọi là cái buzzer rung bần bật đó). Anh ấy nói: "Đây là lời hứa của tôi, khi nào ly của bạn xong, cái phiếu này sẽ báo." Bạn cầm cái phiếu đó, đi ra bàn ngồi lướt TikTok, tám chuyện với bạn bè, thậm chí order thêm đồ ăn vặt khác. Bạn đang "chờ đợi" (await) cái phiếu đó rung, nhưng không phải chờ một cách thụ động, mà bạn vẫn làm được tỉ thứ khác.

Cái "phiếu chờ" đó, chính là một awaitable object trong Python. Nó là bất kỳ thứ gì mà bạn có thể dùng từ khóa await để "đợi" cho nó hoàn thành, trong khi chương trình vẫn có thể làm những việc khác.

Trong Python, awaitable chủ yếu là:

  • Coroutines: Đây là những hàm được định nghĩa bằng async def. Khi bạn gọi một hàm async def, nó không chạy ngay mà trả về một coroutine object – một "lời hứa" sẽ chạy sau.
  • Tasks & Futures: Những thứ này hơi "nâng cao" một tí, thường là các đối tượng được quản lý bởi thư viện asyncio, đại diện cho kết quả của một awaitable nào đó sẽ trả về trong tương lai. Coi nó như cái "phiếu chờ" xịn sò hơn, có thể theo dõi trạng thái, hủy bỏ, v.v.

Tóm lại: awaitable là "những thứ có thể chờ đợi được" (mà không làm treo máy), cho phép chương trình của bạn thực hiện nhiều tác vụ "song song ảo" (concurrently) một cách hiệu quả, đặc biệt là với các tác vụ liên quan đến I/O (như gọi API, đọc/ghi database, tải file...).

2. Code Ví Dụ Minh Hoạ: "Làm bánh mì và pha cà phê"

Để các bạn dễ hình dung, chúng ta sẽ mô phỏng một quán cafe ảo:

import asyncio
import time

# Một awaitable (coroutine) - Giả lập việc pha cà phê mất 3 giây
async def pha_ca_phe():
    print("👨‍🍳 Bắt đầu pha cà phê... ☕")
    await asyncio.sleep(3) # Đây là awaitable, mô phỏng thời gian chờ I/O
    print("✅ Cà phê đã xong! ✨")
    return "Latte đá"

# Một awaitable (coroutine) - Giả lập việc làm bánh mì mất 2 giây
async def lam_banh_mi():
    print("👨‍🍳 Bắt đầu làm bánh mì... 🍞")
    await asyncio.sleep(2) # Đây cũng là awaitable
    print("✅ Bánh mì đã xong! 🥖")
    return "Bánh mì kẹp"

# Hàm chính để điều phối
async def nha_hang_cua_creyt():
    print("Chào mừng đến với Nhà Hàng của Creyt!")

    # Chúng ta "await" từng món một, nhưng vì chúng là awaitable,
    # asyncio sẽ quản lý để chúng chạy "xen kẽ" nhau
    # khi có thời gian chờ (asyncio.sleep).
    
    # Giả sử khách A gọi cà phê và bánh mì.
    # Anh Creyt sẽ nhận 2 "lời hứa" này
    promise_ca_phe = pha_ca_phe() # Trả về coroutine object (awaitable)
    promise_banh_mi = lam_banh_mi() # Trả về coroutine object (awaitable)

    print("\nTrong lúc chờ đồ ăn, mình lướt TikTok tí...")
    await asyncio.sleep(1) # Lướt TikTok 1 giây trong lúc chờ

    # Bây giờ, anh Creyt mới "chờ" từng lời hứa được thực hiện
    ca_phe = await promise_ca_phe # Đợi cà phê xong
    banh_mi = await promise_banh_mi # Đợi bánh mì xong

    print(f"\nKhách hàng nhận được: {ca_phe} và {banh_mi}!")
    print("Tổng thời gian đợi thực tế (không tính TikTok): ~3 giây (vì chạy xen kẽ)")

# Chạy chương trình bất đồng bộ
if __name__ == "__main__":
    start_time = time.time()
    asyncio.run(nha_hang_cua_creyt())
    end_time = time.time()
    print(f"\nTổng thời gian toàn bộ chương trình: {end_time - start_time:.2f} giây")

Giải thích:

  • pha_ca_phe()lam_banh_mi() là các hàm async def, nên khi gọi chúng (ví dụ: pha_ca_phe()), chúng sẽ trả về một coroutine object – một awaitable.
  • await asyncio.sleep(X): Đây là một awaitable khác, cho phép chương trình "ngủ" X giây mà không chặn toàn bộ ứng dụng. Trong lúc đó, asyncio có thể chuyển sang chạy các awaitable khác.
  • await promise_ca_phe: Khi gặp await, chương trình sẽ "treo" việc thực thi hàm nha_hang_cua_creyt TẠI ĐIỂM ĐÓ, và chờ promise_ca_phe hoàn thành. NHƯNG, nó không chặn toàn bộ luồng chính. asyncio sẽ tìm các awaitable khác (như promise_banh_mi nếu nó chưa xong) để chạy xen kẽ.
  • Kết quả là, thay vì đợi 3 giây cho cà phê, rồi 2 giây cho bánh mì (tổng 5 giây), chúng ta chỉ mất khoảng 3 giây (thời gian của tác vụ lâu nhất) vì chúng chạy "xen kẽ" nhau.
Illustration

3. Mẹo & Best Practices từ Giảng viên Creyt

  • await đúng chỗ, đúng việc: Chỉ await khi bạn muốn chờ một awaitable hoàn thành. Đừng await một cách vô tội vạ. Nhớ, await là điểm dừng để asyncio có thể "nhảy" sang làm việc khác.

  • Phân biệt asyncio.sleep()time.sleep():

    • await asyncio.sleep(X): "Ngủ" mà không chặn luồng chính. Rất hợp cho async.
    • time.sleep(X): "Ngủ" và chặn toàn bộ luồng. KHÔNG BAO GIỜ dùng time.sleep trong hàm async def nếu bạn không muốn phá hỏng toàn bộ hiệu quả của async/await!
  • Khi nào thì "gom hàng" lại? Dùng asyncio.gather(): Nếu bạn có nhiều awaitable không phụ thuộc vào nhau và muốn chạy chúng song song để lấy kết quả cùng lúc, hãy dùng asyncio.gather(). Nó giống như bạn đưa một lúc 3 cái phiếu chờ cho 3 món khác nhau, rồi ngồi đợi cả 3 rung lên cùng lúc.

    # Ví dụ dùng asyncio.gather()
    async def order_full_combo():
        print("\nKhách hàng order Full Combo!")
        # Tạo danh sách các "lời hứa"
        promises = [
            pha_ca_phe(),
            lam_banh_mi(),
            asyncio.sleep(1) # Một cái awaitable khác
        ]
    
        # Chờ tất cả các lời hứa này hoàn thành cùng lúc
        results = await asyncio.gather(*promises)
        print(f"\nFull Combo đã xong: {results[0]}, {results[1]} sau {results[2]} giây chờ thêm.")
        # Lưu ý: kết quả của asyncio.sleep(1) là None
    
    # Để chạy, bạn gọi: asyncio.run(order_full_combo())
    
  • Cẩn thận với blocking code: Nếu trong hàm async def của bạn có một đoạn code tính toán cực nặng (CPU-bound) hoặc gọi một thư viện chặn (blocking I/O) mà không phải là awaitable, nó sẽ làm treo toàn bộ vòng lặp sự kiện. Nếu bắt buộc phải dùng, hãy "đẩy" nó sang một thread hoặc process khác bằng loop.run_in_executor().

4. Ứng dụng thực tế của awaitable

Hệ sinh thái async/await trong Python đã phát triển cực kỳ mạnh mẽ, và awaitable là trái tim của nó. Các ứng dụng "xịn xò" mà bạn thấy hàng ngày đã và đang dùng nó:

  • Web Frameworks tốc độ cao: FastAPI, Sanic, Starlette. Các API server này có thể xử lý hàng ngàn request mỗi giây mà không "đổ mồ hôi" vì chúng dùng async/await để quản lý các tác vụ I/O (như đọc database, gọi microservices khác) một cách hiệu quả.
  • Database Drivers bất đồng bộ: asyncpg (PostgreSQL), aiomysql, motor (MongoDB). Giúp ứng dụng giao tiếp với database mà không bị chặn.
  • HTTP Clients "siêu tốc": httpx, aiohttp. Tải hàng trăm trang web cùng lúc để crawl data hay kiểm tra trạng thái là chuyện nhỏ.
  • Bots (Discord, Telegram): Hầu hết các thư viện xây dựng bot hiện đại đều dùng async/await để xử lý các sự kiện đến từ server chat một cách nhanh chóng, không bỏ lỡ tin nhắn nào.
  • Microservices & Event-driven Architectures: Trong các hệ thống lớn, các service cần giao tiếp với nhau mà không làm chậm toàn bộ hệ thống. awaitable giúp việc này trở nên mượt mà hơn bao giờ hết.

5. Nên dùng awaitable cho case nào?

  • Nên dùng khi:
    • Ứng dụng của bạn cần xử lý nhiều tác vụ I/O-bound đồng thời: gọi nhiều API, truy vấn nhiều database, tải/ghi nhiều file, xử lý nhiều kết nối mạng (web server, chat server).
    • Bạn muốn ứng dụng của mình "phản ứng nhanh", không bị lag hay "đứng hình" khi chờ đợi một tác vụ nào đó hoàn thành.
    • Xây dựng các hệ thống thời gian thực (real-time systems) như chat, game server, IoT dashboards.
  • Không nên dùng khi:
    • Tác vụ của bạn chủ yếu là CPU-bound (tính toán nặng, xử lý dữ liệu lớn trong bộ nhớ mà không liên quan đến I/O). async/await không làm cho CPU tính toán nhanh hơn; nó chỉ giúp quản lý thời gian chờ đợi hiệu quả hơn. Đối với CPU-bound, bạn nên nghĩ đến multiprocessing hoặc threading (nếu không có GIL issues).
    • Ứng dụng của bạn quá đơn giản, không có nhiều tác vụ I/O cần xử lý đồng thời. Đôi khi, việc áp dụng async/await có thể làm tăng độ phức tạp không cần thiết.

awaitable không phải là viên đạn bạc cho mọi vấn đề, nhưng nó là một công cụ cực kỳ mạnh mẽ khi bạn muốn xây dựng những ứng dụng Python "nhanh như chớp" và "mượt mà như nhung" trong thế giới số đầy biến động này. Hãy luyện tập và làm chủ nó, Gen Z nhé!

Thuộc Series: Python

Bài giảng này được tự động xuất bản ngẫu nhiên từ thư viện kiến thức. Đừng quên đón xem các Từ khoá Hướng Dẫn tiếp theo nhé!

#tech #cyberpunk #laravel
Chỉnh sửa bài viết

Bình luận (0)

Vui lòng Đăng Nhập để Bình luận

Hỗ trợ Markdown cơ bản
Nguyễn Văn A
1 ngày trước

Tính năng này đỉnh quá ad ơi, chờ mãi mới thấy một blog Tiếng Việt có UI/UX xịn như vầy!