Python - async & await

History

Module

  • asynchat module 現在還保留是為了 backwards compatibility,現在建議使用 asyncio

Example

async keyword 用來把 function 變成 coroutine, 在 coroutine 內可以使用 await 來暫停執行, 等待其他事情完成。

  • async function 回傳的是 coroutine object

  • await 只能用於 async function 內

  • await with 後的 context manager 需要 __aenter____aexit__

  • await for 後的 iterable variable 需要 __aiter____anext__

>>> async def f():
...     pass
...
>>> x = f()
>>> x
<coroutine object f at 0x7f11f9c19ca8>

>>> import asyncio
>>> asyncio.iscoroutine(x)
True
>>> asyncio.iscoroutinefunction(f)
True

>>> class FakeCoro:     # asyncio.iscoroutine() uses collections.abc.Coroutine
...     def send(self, value): pass
...     def throw(self, typ, val=None, tb=None): pass
...     def close(self): pass
...     def __await__(self): yield
...
>>> asyncio.iscoroutine(FakeCoro())
True

>>> class Awaitable:
...     def __await__(self):
...         return ('Hello ~', 'XD')
...
>>> @asyncio.coroutine
... def g():
...     return Awaitable()
...
>>> coro = g()
>>> coro.send(None)
'Hello ~'
>>> coro.send(None)
'XD'
>>> coro.send(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> coro.close()
>>> coro = g()
>>> coro.close()
>>> coro.send(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

>>> async def bar():
...     return 'O w O'
...
>>> async def foo():
...     return await bar()  # await 只能用在 async function 裡
...
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(foo())
'O w O'
>>> loop.close()

async with :

(async with 後面會在進 block 前先去 call __aenter__ , 接著在離開 block 時 call __aexit__ 來做善後, 藉此可以在 method 中使用 await 來接其他 asynchronous function)

>>> class Manager:
...     async def __aenter__(self):
...         print('__aenter__')
...         return self
...     async def __aexit__(self, *args):
...         print('__aexit__')
...         return True
>>> async def foo():
...     async with Manager() as a:
...         print(42)
>>> import asyncio
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(foo())
__aenter__
42
__aexit__

async for :

(async for 後面會先去 call __aiter__ 來拿 iterator, 接著用 __anext__ 來取下一個值, 藉此可以在 method 中使用 await 來接其他 asynchronous function)

>>> class AsyncIter:
...     def __init__(self):
...         self.iteration = 0
...     async def __aiter__(self):
...         print('__aiter__')
...         return self
...     async def __anext__(self):
...         self.iteration += 1
...         if self.iteration > 10:
...             raise StopAsyncIteration
...         return 42
>>> async def bar():
...     async for i in AsyncIter():
...         print(i)
>>> loop.run_until_complete(bar())
__aiter__
42
42
42
42
42
42
42
42
42
42

asyncio.wait :

(asyncio.wait 可以一次吃很多 coroutine 下去執行、等待)

>>> import asyncio
>>> async def f(time, *args):
...     await asyncio.sleep(time)
...     print('O w O : {}'.format(args))
>>> async def main():
...     result = await asyncio.wait([
...             f(0.5, 124124, 'asd'),
...             f(0.3, 423434343434343434343434343434343434343434343434, 'asd'),
...             f(0.1, 412124, 'das'),
...         ])
...     print(result)
>>> loop = asyncio.get_event_loop()
>>> loop.run_until_complete(main())
O w O : (412124, 'das')
O w O : (423434343434343434343434343434343434343434343434, 'asd')
O w O : (124124, 'asd')
({<Task finished coro=<f() done, defined at <stdin>:1> result=None>, <Task finished coro=<f() done, defined at <stdin>:1> result=None>, <Task finished coro=<f() done, defined at <stdin>:1> result=None>}, set())
>>> import asyncio
>>> tmp = asyncio.create_subprocess_shell('echo "yo"')
>>> loop.run_until_complete(a)
<Process 4037>
yo

Reference