代码之家  ›  专栏  ›  技术社区  ›  jsstuball

future.add_done_callback()的用例是什么?

  •  1
  • jsstuball  · 技术社区  · 7 年前

    我了解如何向未来添加回调方法,并在未来完成时调用它。但是,当您已经可以从协程内部调用函数时,这为什么会有帮助呢?

    回调版本:

    def bar(future):
        # do stuff using future.result()
        ...
    
    async def foo(future):
        await asyncio.sleep(3)
        future.set_result(1)
    
    loop = asyncio.get_event_loop()
    future = loop.create_future()
    future.add_done_callback(bar)
    loop.run_until_complete(foo(future))
    

    async def foo():
        await asyncio.sleep(3)
        bar(1)
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(foo())
    

    第二个版本何时不可用/不适用?

    1 回复  |  直到 7 年前
        1
  •  15
  •   user4815162342    6 年前

    在如图所示的代码中,没有理由使用明确的future和 add_done_callback ,你可以随时 await . 一个更现实的用例是如果情况发生逆转,如果 bar() foo() 并需要访问其结果:

    def bar():
        fut = asyncio.create_task(foo())
        def when_finished(_fut):
            print("foo returned", fut.result())
        fut.add_done_callback(when_finished)
    

    如果这让你想起了“回叫地狱”,那么你就走上了正确的道路- Future.add_done_callback 大致相当于 then 预异步/等待JavaScript承诺的运算符(细节不同是因为 then() 是一个返回另一个承诺的组合符,但基本思想是相同的。)

    asyncio的很大一部分是 实施 like this .

    即使在处理非协程回调时,也很少有好的解决方案 原因 使用 添加\u完成\u回调 ,而不是惯性或复制粘贴。例如,上述函数可以简单地转换为使用 等待 :

    def bar():
        async def coro():
            ret = await foo()
            print("foo returned", ret)
        asyncio.create_task(coro())
    

    这比原文更具可读性,而且 更容易适应更复杂的等待场景。它是 similarly easy 将协程插入较低级别的asyncio管道。

    那怎么办 Future API和 添加\u完成\u回调 ? 我可以想到几个:

    • 书写 new combinators .
    • 将协同程序代码与以更传统的回调风格编写的代码连接起来,例如 this this .
    • 编写Python/C代码,其中 async def 没有现成的。

    为了说明第一点,考虑一下你会怎么做。 实施 函数 asyncio.gather() . 它必须允许传递的协程/未来运行并等待 全部的 添加\u完成\u回调 全部的 没有等待他们的一系列未来。以忽略异常处理和各种功能的最基本形式, gather()

    async def gather(*awaitables):
        loop = asyncio.get_event_loop()
        futs = list(map(asyncio.ensure_future, awaitables))
        remaining = len(futs)
        finished = loop.create_future()
        def fut_done(fut):
            nonlocal remaining
            remaining -= 1
            if not remaining:
                finished.set_result(None)  # wake up
        for fut in futs:
            fut.add_done_callback(fut_done)
        await finished
        # all awaitables done, we can return the results
        return tuple(f.result() for f in futs)
    

    即使你从不使用 添加\u完成\u回调