Python:手把手教你 async 與 await
在 Python 的世界裡,效能一直是大家討論的熱點。當你遇到需要處理大量網路請求、讀寫大檔案或與資料庫頻繁互動時,傳統的「同步」執行方式往往會讓程式卡在那邊等,效率低得驚人。
這時候,async 和 await 就派上用場了!
什麼是異步編程?
想像一下,你正在準備晚餐。
- 同步 (Synchronous):你先把水煮開,盯著鍋子看,等水開了才去切菜。切完菜,你才去熱鍋炒菜。每件事都必須等上一件做完才能開始。
- 異步 (Asynchronous):你先把水放上去煮,趁水還沒開的時候,你跑去切菜。切菜的過程中,如果水開了(發出嗶嗶聲),你就轉身去處理水。你不需要在那邊乾等水開。
在程式中,當我們發送一個網路請求(煮水),CPU 其實是在「等」網路回傳(水開)。異步編程就是讓 CPU 在等待的期間可以先去處理別任務(切菜),從而大大提昇效率。
核心關鍵字:async 與 await
在 Python 3.5 之後,官方引入了這兩個語法糖:
1. async def:定義協程 (Coroutine)
平常我們用 def 定義函數,但如果要定義一個異步函數,我們要在前面加上 async。1
2
3
4
5
6
7import asyncio
async def say_hello():
print("Hello...")
# 這裡會模擬一個耗時操作
await asyncio.sleep(1)
print("...World!")
呼叫一個 async 函數時,它不會立即執行裡面的代碼,而是會回傳一個「協程物件」。
2. await:等待結果
當你執行到一個需要等待的操作(例如 asyncio.sleep 或網路請求)時,使用 await。這會告訴 Python:「我現在要在這裡等一下,你可以先去忙別的事,等這件事處理好了再回來找我。」
注意:
await只能在async定義的函數內部使用。
實際範例:比比看誰快
假設我們要執行三個耗時 1 秒的任務。
同步版本 (慢)
1 | import time |
異步版本 (快)
1 | import asyncio |
可以看到,異步版本幾乎只花了 1 秒就完成了所有任務,因為它在等待第一個任務時,就已經開始處理第二個和第三個了。
常見的陷阱:為什麼我的程式還是很慢?
很多人剛開始用 asyncio 時會遇到一個問題:為什麼加上了 async 還是沒變快?
最常見的原因是在 async 函數中使用了「同步」的阻塞代碼。1
2
3
4
5
6
7import asyncio
import time
async def broken_task():
# 錯誤示範!
time.sleep(1) # 這會把整個 Event Loop 鎖住,別人在這段時間也動彈不得
await asyncio.sleep(1)
如果你在異步環境中用了 time.sleep()、requests.get() 或任何不支援異步的資料庫驅動,你的程式依然會像同步執行一樣一個一個排隊。
結語
async 和 await 是 Python 處理高併發 (High Concurrency) 的利器。雖然它的學習曲線稍微陡峭一點,但一旦掌握了「不要阻塞 Event Loop」這個核心觀念,你就能寫出極其高效的 Python 程式。
下次如果你要爬取上百個網頁,或開發一個高效的後端 API,別忘了試試看 asyncio 喔!