先來複習一下前面的教學:
上上一篇的python的asyncio模組(二):異步程式設計基本概念,我們學到異步程式設計的3個基本概念:
上一篇的python的asyncio模組(三):建立Event Loop和定義協程,我們得知如何定義asyncio程式的兩個最基本的要素:
其中Coroutine能中途暫停和恢復運作的特性可以很好的實現異步程式設計中的Event與Callback兩個概念,又能避免以前殘害javascript開發者許久的callback hell。
因為當我們要在一個Coroutine裡中途監聽某一個Event發生後再執行後續行為時,只需要用await關鍵字來等待某個Event發生:
def a_coroutine():
# do something...
...
await listen_event_happend()
# do something after event happend...
...
而不需要明確定義需要監聽的Event以及對應的Callback,並註冊到Event loop裡,這就很可能會導致callback hell的狀況:
def a_coroutine():
# do something...
...
def do_something_after_event_happend():
# do something after event happend...
...
register_to_event_loop(listen_event_happend, do_something_after_event_happend)
若上面的例子還聞不出callback hell的味道,可以請讀者想想看以下的例子,要如何改成需要明確定義Event和Callback的寫法:
def a_coroutine():
# do something...
...
await listen_event1_happend()
# do something after event1 happend...
...
await listen_event2_happend()
# do something after event2 happend...
...
await listen_event3_happend()
# do something after event3 happend...
...
因為Coroutine和一般的函數特性有一些本質上的差異,他可以中途暫停等待某個事件發生,所以我們對於一個Coroutine的業務操作,會比一般的函數還要更多,所以asyncio所衍生出來的基本語法就看起來非常的多樣且複雜。
但若掌握了一些基本的方向,對於常用的基本語法,我們還是能逐步的了解這些語法為何被發明出來且如何被使用。
我們必須先了解,當我們定義了一個Coroutine function後,一直到這個function被Event loop執行的過程中,這個function會經歷幾次的對象轉換:
Coroutine function --> Coroutine object --> Task
我們可以從以下例子了解到其中的對象轉換過程:
# python3.5
# ubuntu 16.04
import asyncio
async def func():
print('func start')
await asyncio.sleep(1)
print('func end')
loop = asyncio.get_event_loop() # 建立一個Event loop
print(func) # 原本是一個coroutine function
obj=func()
print(obj) # 執行之後回傳一個coroutine object
task = asyncio.ensure_future(obj)
print(task) # 經過ensure_future包裝成一個Future對象(也可以說是一個Task對象,兩者的差別下一篇教學會說明)
loop.run_until_complete(task) # 把task丟入Event loop執行,並在task結束時結束Event loop
'''
Output:
<function func at 0x7f28e365aa70>
<coroutine object func at 0x7f28e365a9e0>
<Task pending coro=<func() running at test.py:3>>
func start
func end
'''
當我們執行coroutine function之後,並不會真得到執行的結果,事實上function也沒有執行,而是得到一個協程對象(coroutine object)。
然後ensure_future會把coroutine object封裝程一個task對象,在python的asyncio模組(三):建立Event Loop和定義協程就有提到過,這個對象可以儲存任務執行時的狀態與環境,所以coroutine才能暫停與恢復運行。
2020/05/24更新
coroutine object本身就能儲存任務執行時的狀態與環境,而task對象是作為Event loop和coroutine的溝通介面
然後這個Task對象才能真正丟入Event loop下去執行,但就上面的程式來說,我們也可以把coroutine object作為參數丟進loop.run_until_complete,只是在run_until_complete裡也還是會把coroutine object轉成Task對象再去執行。
接下來我們來講一下和Event loop有關的一些常用基本語法:
這個函數接收一個Task對象,並指定這個Event loop若執行完這個被傳入Task就會停止,不管loop裏面還有沒有其他的Task正在執行當中或是等待執行。
而如同上面所說,這個參數也可以傳入coroutine object,只是在放入Event loop執行前,這個coroutine object會先被轉成Task對象。
顧名思義,這個函數會讓一個loop無止境的運行下去,就算當中所有Task已經被執行完畢只剩下空的loop在運轉,除非在程式中呼叫loop.stop()才能夠停止這個Event loop。
這個函數會接收一個coroutine object,並包裝成一個Task對象,同時把這個Task註冊到這個Event loop中等待執行。
而當loop.run_until_complete或loop.run_forever被呼叫時,這些被註冊進去的Task才會被執行。
這個函數不接收任何的參數,然後會回傳一個空的Future對象,並註冊到Event loop裏面等待執行。
目前可以視Future對象和Task對象為一樣的東西,兩者定義上的差別下一次會說明。
判定一個Event loop是否還在運作。
判定一個Event loop是否已經被close掉。
stop一個Event loop。
close掉一個Event loop。
下一篇教學:
python的asyncio模組(五):Future對象與Task對象
參考資料:
https://www.jianshu.com/p/b5e347b3a17c
https://www.dongwm.com/post/understand-asyncio-2/
https://juejin.im/post/5c2f12ce6fb9a04a006f2659