基础
- 定义协程(coroutine)的两种方式:
async def或使用generator实现。 基于generator的协程应该使用@asyncio.coroutine修饰,但这并不是严格要求的,基于generator的协程使用yield from语法。
coroutine这个词有两种意思:- 定义协程的函数
使用
async def或@asyncio.coroutine。无歧义的称呼是coroutine function,isroutinefunction()返回True。 - 调用协程函数获得的对象
这个对象表示一个可结束的计算或I/O操作。无歧义的称呼是
coroutine object,iscoroutine()返回True。
- 定义协程的函数
使用
协程可以做的事:
result = await future或result = yield from future暂停协程,直到future完成,然后返回future的结果,或者抛出一个异常。result = await coroutine或result = yield from coroutine等待另一个协程产生结果(或者抛出一个异常)。coroutine表达式必须为对另一个协程的调用。return expersssion产生结果,结果传递给那些使用await或yield from等待此协程的协程。raise exception抛出异常。
asyncio
asyncio.get_event_loopasyncio.ensure_future(coro_or_future, *, loop=None)将coroutine object包装在future中,返回Task对象。BaseEventLoop.run_until_complete(future)阻塞,直到future完成,返回future的结果。 如果参数是一个coroutine object,则使用ensure_future()包装一下。
本质
添加任务到事件循环中
每一次循环,从队列中取出一个任务,对其调用send,让其继续执行,将send的结果继续添加到任务队列中。如果调用send抛出了
StopIteration异常,则表示任务已经完成了,则不再添加到队列中。await future,future 抛出的StopIteration由await捕获,并从StopIteration对象的第一参数中获取
future的结果,作为await的返回值,此时被等待的future已经完成,继续执行当前coroutine之后的代码。如果future没有抛出异常,则future中
yield的结果经由这个await(或者yield from)向上传递,一直到调用send的那个语句中(一般是事件循环中),作为send的返回值。所以一直都是最深层次的任务和事件循环在打交道,与途中的await没有关系,只有当最底层的future完成之后,才开始执行其上一层的任务。
疑问
第一个
send(None)或者next(coro)的返回值是什么?第一次 send 使 generator 开始执行,直到遇到第一个 yield
此时 generator 让出执行权,将 yield 表达式的值作为 send 的返回值,此时 generator 内部 yield 左边的赋值并没有进行,而是在这之间就 yield(让步)了。
第二次调用 send(something), 则从 yield 赋值语句开始恢复,send 的参数作为 yield 的返回值,赋给左边的变量,继续执行。
yield 与 yield from 有什么区别?
函数中的 yield 使函数成为 generator,当 generator 到达末尾时(自然结束或遇到 return 语句),会抛出
StopIteration异常。Python3.3 之后,return 语句的参数会成为
StopIteration的 value 参数(raise StopIteration(value=something))。普通的由 yield 创造的 generator 一般会忽略 StopIteraion.value,也就是忽略reurn的值,只将StopIteration作为generator exhausted的条件。而
yield from会捕获StopIteraion异常,并获取其 value 参数,作为自己的返回值。看上去没什么大不了,但却是后来await的基础。??
如果yield from future, future 没有 return,那么 future 中 yield 产生的值都会经过 yield from 向更高层传递,最终到达事件循环,与 yield from 一点关系也没有。当 futurn return时,yield from 很开心,终于不用做传话人了,于是获取 futurn 的结果,作为自己的返回值,也就意味着这个future resolve了,这个异步任务完成了。
本来使用yield可以产生任意类型的值,yield from都会将其逐步传递到最顶层作为 send 的返回值。但是因为最顶层一般是事件循环,它需要将 send 的结果,继续添加到任务队列中,之后再从队列中将其取出,对其调用 send 方法(或next)让其继续执行。所以这个对象就必须有一些特殊要求,这个要求就是awaitable???? Future? Task?
await与yield from的区别?
await比yield from只多了一步参数检查:await只接受awaitable对象,包括:
- 一个 native coroutine object returned from native coroutine function
- generator-based coroutine object returned from a functin decoreated with
asyncio.coroutine() - an object with an __await__ method returning an iterator.
任何 yield from 链都是以 yield 结尾的,这是 Future 实现的基础。每个await 都是被 await链底端的 yield 暂停的。
async 与 await 语法
- async def 函数永远是协程,即使它不包含await表达式
- async 函数中使用 yield 或 yield from 是语法错误
- async def 定义的是 native coroutine,设置了
CO_COROUTINE标志 - generator-based coroutine 与 native coroutine 兼容,设置了
CO_ITERABLE_COROUTINE标志 - 在async def 函数外使用 await 是语法错误
- 为 await 传递非awaitable对象会报 TypeError