[TBC]
在
Python 3.5
加入了 async def
的語法支援:用來產生
coroutine
函數、而其中可以再使用
await
、async for
與 async with
語法 (這些語法也僅能在 coroutine 函數內使用)。
Python 3.7
之後
async
跟 await
則變成保留字。一個最簡單的 coroutine 如下:
# define a coroutine function
async def foo():
return 'ping'
if __name__ == '__main__':
# get the coroutine
f = foo()
# >>> <class 'coroutine'>
print(type(f))
try:
# method of builtins.coroutine instance
# return next iterated value or raise StopIteration.
f.send(None)
except StopIteration as e:
# >>> ping
print(e.value)
在上面的函數 foo
中只回傳了一個字串,因此透過 built-in 函數 send
就不用送額外的參數進去。因為在 generator
的角度來看這個 coroutine 已經結束,因此使用 StopIteration
將結果回傳。
如果需要等待一個 coroutine 的結果,就可以使用 await
保留字:在 coroutine 中如果再呼叫另一個 coroutine
則需要使用 await 保留字,如果沒有就跟直接呼叫 coroutine 一樣出現
RuntimeError
。一個簡單的 await 可以使用
兩個 coroutine 組合而成:
async def x():
return 'x'
async def y():
try:
await x().send(None)
except StopIteration as e:
return f'{e} -> y'
try:
y().send(None)
except StopIteration as e:
print(f'finish {e}')
await 可以使用 asyncio.sleep(1)
來將目前的 coroutine 暫停,並等待一秒的時間。當使用 asyncio.sleep(0)
的時候等價於直接
yield
一個空的 coroutine,除此之外則是使用 create_future
產生一個新的
Future
並等待指定秒數直到結束。
Coroutine / Task / Future
在使用 coroutine 的時候同時會了解兩個新的名詞: Task 與 Future 。對於 Task 是屬於 asyncio 裡的一個 class 實作, 按照描述是一個用來執行 coroutine、 Future-Like 的物件並且非 thread-safe :如果 coroutine 在 Future 內觸發 await 就會暫停 (suspends)、等到 Future 狀態改為 done 之後才會恢復 (resumes) 執行狀態。
而 Future 則代表一個 aync 的執行結果 (同時也為非 thread-safe)。一個 Future 同時也是一個 awaitable 物件,代表一個 coroutine 可以等待直到 1) 回傳結果 2) 拋出例外 或者 3) 取消。
types.coroutine
在早期的 code 中可以使用 types.coroutine
這個函數將 generator 轉換成 coroutine (如果已經是 coroutine 則直接回傳)
。作法在
程式碼
中可以看到是透過底層 C 實作替換、否則就透過透過 functools.wraps
產生 coroutine
(但是可能最後不是 coroutine)。在下面的程式就是透過 types.coroutine 將一個 generator 轉換成 coroutine:
import types
@types.coroutine
def inner():
yield 1
return 2
async def outer():
print(await inner())
fn = outer()
# >>> 1, from yield 1
fn.send(None)
# >>> 2, from return 2
fn.send(None)
# >>> StopIteration
fn.send(None)
async-generator
同理,可以在 async 裡面使用 yield
保留字而非 return
來產生一個 async_generator:根據
PEP 525
的描述,
這是一個 generator 的非同步實作。
async-for
在 coroutine 函數中使用 async-for
語法,代表在 iter 部份中被非同步呼叫。語法等價於將 iter 使用
await
語法呼叫。
async-with
同 async-for
,使用 async-with
表示在 with-statement 中的語法使用非同步方式呼叫。