如何将基于回调的异步函数转换为异步生成器

时间:2017-04-29 18:22:07

标签: python python-3.x asynchronous python-asyncio asynccallback

目前可以通过提供将在每个传入连接上触发的回调来启动asyncio服务器:

async for

我想摆脱回调并改用async for reader, writer in my_start_server(host, port): # this should be invoked each time a new connection is made pass ,所以看起来像这样:

async def my_start_server(host, port):
    async def on_connection(reader, writer):
        # here I have to somehow yield (reader, writer) tuple
        # but if I do just `yield (reader, writer)`, it would 
        # make `on_connection` itself a generator, but would 
        # not affect `my_start_server`
        pass

    server = await asyncio.start_server(on_connection, host, port)

不幸的是,它似乎并不容易:

__aiter__

我曾考虑过在那里实现Auth::guard('api')->user()实现的类,但结果似乎过于复杂。那么,这是唯一的方法,还是我错过了将异步回调转换为异步生成器的任何简单方法?

1 个答案:

答案 0 :(得分:1)

正如文森特所注意到的那样,你不应该使用async for来获取连接,因为它内部的代码会阻止其他连接处理。将每个连接视为应该运行的单独任务,而不管其他任何连接。

以下示例yield如何使用my_start_server使用队列,这也表明我们仍会返回某种on_connection

async def my_start_server(host, port):
    queue = asyncio.Queue()

    async def put(reader, writer):
        await queue.put((reader, writer,))

    await asyncio.start_server(put, host, port)

    while True:
        yield (await queue.get())

async for (reader, writer) in my_start_server(host, port):

    # If we will just handle reader/writer here, others will be suspended from handling.
    # We can avoid it starting some task,
    # but in this case 'handle' would be nothing different from 'on_connection'

    asyncio.Task(handle(reader, writer))