python asyncio add_done_callback with async def

时间:2017-06-03 14:47:30

标签: python python-3.x async-await python-asyncio coroutine

我有两个函数:第一个函数def_a是一个异步函数,第二个函数是def_b,它是一个常规函数,调用结果为def_a使用add_done_callback函数回调。

我的代码如下所示:

import asyncio

def def_b(result):
    next_number = result.result()
    # some work on the next_number
    print(next_number + 1)

async def def_a(number):
    await some_async_work(number)
    return number + 1

loop = asyncio.get_event_loop()
task = asyncio.ensure_future(def_a(1))
task.add_done_callback(def_b)
response = loop.run_until_complete(task)
loop.close()

它完美无缺。

当第二个函数def_b变为异步时,问题就开始了。现在它看起来像这样:

async def def_b(result):
    next_number = result.result()
    # some asynchronous work on the next_number
    print(next_number + 1)

但现在我无法将其提供给add_done_callback函数,因为它不是常规函数。

我的问题是 - 如果def_b是异步的,我可以如何向add_done_callback函数提供def_b吗?

3 个答案:

答案 0 :(得分:20)

add_done_callback被视为“低级别”界面。使用协同程序时,您可以通过多种方式chain them,例如:

import asyncio


async def my_callback(result):
    print("my_callback got:", result)
    return "My return value is ignored"


async def coro(number):
    await asyncio.sleep(number)
    return number + 1


async def add_success_callback(fut, callback):
    result = await fut
    await callback(result)
    return result


loop = asyncio.get_event_loop()
task = asyncio.ensure_future(coro(1))
task = add_success_callback(task, my_callback)
response = loop.run_until_complete(task)
print("response:", response)
loop.close()

请注意,如果您的未来会引发异常,add_done_callback仍会调用回调(但调用result.result()会引发异常)。

答案 1 :(得分:1)

这仅适用于将来的一项工作,如果您有多个异步工作,它们将相互阻塞,一种更好的方法是使用asyncio.as_comleted()来迭代将来的列表:

import asyncio

async def __after_done_callback(future_result):
    # await for something...
    pass

async def __future_job(number):
    await some_async_work(number)
    return number + 1

loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(__future_job(x)) for x in range(100)]  # create 100 future jobs

for f in asyncio.as_completed(tasks):
    result = await f
    await __after_done_callback(result)

loop.close()

答案 2 :(得分:0)

你可以试试 aiodag 库。它是围绕 asyncio 的一个非常轻量级的包装器,可以抽象出您通常必须考虑的一些异步管道。从这个例子中,你将无法判断事情是异步运行的,因为它只是一个依赖于另一个任务的任务,但它都是异步运行的。

import asyncio
from aiodag import task

@task
async def def_b(result):
    # some asynchronous work on the next_number
    print(result + 1)

@task
async def def_a(number):
    await asyncio.sleep(number)
    return number + 1

async def main():
    a = def_a(1)
    b = def_b(a)  # this makes task b depend on task a
    return await b

loop = asyncio.get_event_loop()
asyncio.set_event_loop(loop)
response = loop.run_until_complete(main())