
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àmasync def, nó không chạy ngay mà trả về mộtcoroutine 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ộtawaitablenà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()vàlam_banh_mi()là các hàmasync def, nên khi gọi chúng (ví dụ:pha_ca_phe()), chúng sẽ trả về mộtcoroutine object– mộtawaitable.await asyncio.sleep(X): Đây là mộtawaitablekhá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 đó,asynciocó thể chuyển sang chạy cácawaitablekhác.await promise_ca_phe: Khi gặpawait, chương trình sẽ "treo" việc thực thi hàmnha_hang_cua_creytTẠI ĐIỂM ĐÓ, và chờpromise_ca_phehoàn thành. NHƯNG, nó không chặn toàn bộ luồng chính.asynciosẽ tìm cácawaitablekhác (nhưpromise_banh_minế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.

3. Mẹo & Best Practices từ Giảng viên Creyt
-
awaitđúng chỗ, đúng việc: Chỉawaitkhi bạn muốn chờ mộtawaitablehoàn thành. Đừngawaitmột cách vô tội vạ. Nhớ,awaitlà điểm dừng đểasynciocó thể "nhảy" sang làm việc khác. -
Phân biệt
asyncio.sleep()và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ùngtime.sleeptrong hàmasync defnếu bạn không muốn phá hỏng toàn bộ hiệu quả củaasync/await!
-
Khi nào thì "gom hàng" lại? Dùng
asyncio.gather(): Nếu bạn có nhiềuawaitablekhô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ùngasyncio.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 defcủ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ằngloop.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.
awaitablegiú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/awaitkhô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ĩ đếnmultiprocessinghoặcthreading(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/awaitcó thể làm tăng độ phức tạp không cần thiết.
- 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).
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é!