【问题标题】:python asyncio add_done_callback with async defpython asyncio add_done_callback 与 async def
【发布时间】:2017-11-04 19:43:37
【问题描述】:

我有 2 个函数:第一个,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 是异步的,是否有可能以及如何将def_b 提供给add_done_callback 函数?

【问题讨论】:

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


    【解决方案1】:

    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() 会引发异常)。

    【讨论】:

    • 感谢分享,出于好奇,使用确保未来比在 my_callback 中使用 await my_coro 有什么好处
    • ensure_future(coro) 只是将coro() 排队等待稍后执行,不会立即启动coro()
    • asyncio.create_task() 应该在 Python 3.7+ 中使用而不是 asyncio.ensure_future() docs.python.org/3/library/asyncio-task.html#asyncio.create_task
    • 为什么这是正确的答案。我认为在回调函数中使用asyncio.ensure_future 是正确的。
    【解决方案2】:

    这只适用于一个未来的工作,如果你有多个异步工作,它们会互相阻塞,更好的方法是使用 asyncio.as_completed() 来迭代未来的列表:

    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()
    

    【讨论】:

      【解决方案3】:

      你可以试试 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())
      

      【讨论】:

        猜你喜欢
        • 2017-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-03
        • 2021-07-02
        • 2021-09-07
        • 1970-01-01
        相关资源
        最近更新 更多