如何在Python 3.5中使用async/await?

55
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import time

async def foo():
  await time.sleep(1)

foo()

我无法让这个非常简单的例子运行起来:

RuntimeWarning: coroutine 'foo' was never awaited foo()

time.sleep不是协程,因此即使您正确运行事件循环,也无法等待它。此外,如果在事件循环中使用阻塞代码,则会使其再次变为同步。这样做没有任何意义。 - Marek Marczak
2个回答

78

运行协程需要一个事件循环。使用asyncio()创建一个:

import asyncio

# Python 3.7+
asyncio.run(foo())

或者

# Python 3.6 and older
loop = asyncio.get_event_loop()
loop.run_until_complete(foo())

请查看Tasks and Coroutines章节的asyncio文档。如果您已经有一个正在运行的循环,您可以通过创建任务(在Python 3.7+中使用asyncio.create_task(...),在旧版本中使用asyncio.ensure_future(...))并发地运行其他协程。

请注意,time.sleep()不是可等待对象。它返回None,因此您将在1秒后收到异常:

>>> asyncio.run(foo())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/.../lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/.../lib/python3.7/asyncio/base_events.py", line 573, in run_until_complete
    return future.result()
  File "<stdin>", line 2, in foo
TypeError: object NoneType can't be used in 'await' expression

在这种情况下,你应该使用asyncio.sleep()协程代替:
async def foo():
    await asyncio.sleep(1)

该协议与循环合作,以使其他任务能够运行。对于那些没有asyncio等效项的第三方库中的阻塞代码,您可以在执行器池中运行该代码。请参见asyncio开发指南中的运行阻塞代码


如果foo()是来自一个库的函数,是否有可能封装它(而不修改源代码)以满足await的要求? - northtree
1
@northtree:你可以在线程池执行器中运行阻塞代码,以释放 asyncio 事件循环线程。但是要小心那些没有取消方式的阻塞线程。 - Martijn Pieters

3
如果您已经有一个正在运行的循环(包含其他任务),您可以通过以下方式添加新任务:
asyncio.ensure_future(foo())

否则你可能会遇到问题。
The event loop is already running

错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接