了解asyncio:异步与同步回调

时间:2016-08-29 20:18:40

标签: python asynchronous callback python-asyncio

关于asyncio的一个非常特别的事情我似乎无法理解。这就是异步和同步回调之间的区别。让我举几个例子。

Asyncio TCP示例:

class EchoServer(asyncio.Protocol):

def connection_made(self, transport):
    print('connection made')

def data_received(self, data):
    print('data received: ', data.decode())

def eof_received(self):
    pass

def connection_lost(self, exc):
    print('connection lost:', exc)

Aiohttp例子:

async def simple(request):
    return Response(text="Simple answer")

async def init(loop):
    app = Application(loop=loop)
    app.router.add_get('/simple', simple)
    return app

loop = asyncio.get_event_loop()
app = loop.run_until_complete(init(loop))
run_app(app, loop=loop)

这两个例子在功能上非常相似,但它们似乎都以不同的方式进行。在第一个示例中,如果要通知某个操作,请指定同步函数(EchoServer.connection_made)。但是,在第二个示例中,如果要通知某个操作,则必须定义异步回调函数(simple)。

我想问一下这两种类型的回调有什么区别。我理解常规函数和异步函数之间的区别,但我无法理解回调差异。例如,如果我想编写像aiohttp这样的异步API,我会有一个函数可以做某事并在此之后调用回调函数,我将如何决定是否应该要求异步函数作为参数传递还是只是常规同步传递?

1 个答案:

答案 0 :(得分:1)

aiohttp示例中,您可以从simple web-handler执行异步调用:访问数据库,发出http请求等。

Protocol.data_received()中,您应该只调用常规同步方法。

<强> UPD

Protocol回调应该是设计同步的。 它们是同步和异步之间的低级桥接。 您可以从它们调用异步代码,但它需要非常棘手的编码。 套接字等的用户级asyncio API是流:https://docs.python.org/3/library/asyncio-stream.html

当你引入自己的回调系统时,你可能需要异步回调,除非你是%100确定回调永远不会想要调用异步代码。

常规函数(def)和协同程序(async def)具有不同的签名。很难更改所需的签名,特别是如果您的代码已作为库发布而您无法控制回调的所有用户。

P.S。

任何公共API方法都是如此。

我在开发我的库时学到的最难的一课是:.close()方法应该是协程,即使它最初只调用同步函数,例如socket.close()

但是稍后您可能会想要添加一个正常关闭,这需要等待当前活动完成等等。

如果您的用户已将{api}称为obj.close(),则他们应使用await obj.close()向后不兼容更改!