8.3. 事件迴圈

事件迴圈是 asyncio 在底層運行的引擎。它維護一份程式中每個任務的清單,請每個任務執行到該任務的下一個 await,然後移往下一個已準備好的任務。當沒有已準備好的任務時,它會等待 — 實際的等待正是讓 CPU 可供韌體執行其他事物、並讓省電睡眠得以啟動的原因 — 直到某個任務所等待的事物變為可用,然後恢復該任務。如此永遠重複。

大多數應用程式從不直接與迴圈互動。迴圈是呼叫 asyncio.run() 的結果;應用程式撰寫協程、將它們排程為任務,其餘的則交給迴圈處理。

8.3.1. asyncio.run() 實際上做了什麼

單一一次呼叫:

asyncio.run(main())

是迴圈代表應用程式所管理的一段較長序列的簡寫:

  1. 若事件迴圈尚不存在則建立它。

  2. 將所提供的協程包裝在任務中,並將其排程為迴圈的頂層進入點。

  3. 運行迴圈 — 逐步執行已準備好的任務、在沒有任務準備好時等待、在各任務的 await 完成時恢復它們 — 直到頂層任務回傳或引發例外。

  4. 取消應用程式所建立、且仍在運行中的任何任務。

  5. 回傳頂層協程所回傳的任何值(或重新引發它所引發的任何例外)。

8.3.2. 每個程式單一迴圈

MicroPython 的 asyncio 有一個事件迴圈,就這樣。無法建立全新的迴圈,也無法將一個迴圈巢狀於另一個之中。從一個已在迴圈上運行的協程內部呼叫 asyncio.run() 是錯誤的;迴圈已經存在了,協程只需 await 它想啟動的任何事物即可。

實務上,這項規則與前一頁的結尾那一行相同:每個程式恰好有一次 asyncio.run() 呼叫,位於最頂層,背後是單一一個 async def main()。其他一切都存在於 main 內部。

8.3.3. 直接存取迴圈

對於應用程式需要直接接觸迴圈本身的少數情況 — 大多是診斷與例外處理常式 — asyncio.get_event_loop() 會回傳 Loop 物件。從那裡,應用程式可以安裝自訂的例外處理常式、檢視迴圈正在做什麼,或(極偶爾地)直接呼叫 create_task() 而非 asyncio.create_task()(兩者是相同的操作)。

Loop 所公開的完整方法集 — run_forever()stop()set_exception_handler() 等其餘方法 — 會在本節稍後的 迴圈控制 一頁中介紹。在那之前,asyncio.run(main()) 就是應用程式所需要的全部。