【问题标题】:Combine awaitables like Promise.all结合像 Promise.all 这样的等待对象
【发布时间】:2016-03-26 10:38:55
【问题描述】:

在异步 JavaScript 中,使用Promise.all 很容易并行运行任务并等待所有任务完成:

async function bar(i) {
  console.log('started', i);
  await delay(1000);
  console.log('finished', i);
}

async function foo() {
    await Promise.all([bar(1), bar(2)]);
}

// This works too:
async function my_all(promises) {
    for (let p of promises) await p;
}

async function foo() {
    await my_all([bar(1), bar(2), bar(3)]);
}

我试图用python重写后者:

import asyncio

async def bar(i):
  print('started', i)
  await asyncio.sleep(1)
  print('finished', i)

async def aio_all(seq):
  for f in seq:
    await f

async def main():
  await aio_all([bar(i) for i in range(10)])

loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()

但它按顺序执行我的任务。

等待多个可等待对象的最简单方法是什么? 为什么我的方法不起作用?

【问题讨论】:

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


    【解决方案1】:

    相当于使用asyncio.gather:

    import asyncio
    
    async def bar(i):
      print('started', i)
      await asyncio.sleep(1)
      print('finished', i)
    
    async def main():
      await asyncio.gather(*[bar(i) for i in range(10)])
    
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()
    

    为什么我的方法不起作用?

    因为当您awaitseq 中的每个项目时,您会阻止该协程。所以本质上,你有伪装成异步的同步代码。如果您真的想要,您可以使用loop.create_taskasyncio.ensure_future 实现您自己的asyncio.gather 版本。

    编辑

    原始答案使用较低级别的asyncio.wait

    【讨论】:

    • 也请提及 asyncio.gather
    • waitgather 之间的主要区别是wait 允许timeout 参数吗?
    • asyncio.wait 是更底层的等待方式。同样在 3.8 中,直接通过协程等待已被弃用。请使用 asyncio.gather 重写您的答案或使用 sn-p 更新,因为它更接近 Promise.all。
    【解决方案2】:

    我注意到,如果我们想要有序的结果,asyncio.gather() 可能是比 asyncio.wait() 更好的等待方式。

    如文档所示,来自 asyncio.gather() 方法的结果值的顺序对应于 aws 中可等待对象的顺序。但是,来自 asyncio.wait() 的结果值的顺序不会做同样的事情。你可以测试它。

    【讨论】:

      【解决方案3】:

      https://docs.python.org/3/library/asyncio-task.html#asyncio.gather

      asyncio.gather() 将返回每个异步函数调用的输出列表。

      import asyncio
      
      async def bar(i):
          print('started', i)
          await asyncio.sleep(1)
          print('finished', i)
          return i
      
      async def main():
          values = await asyncio.gather(*[bar(i) for i in range(10)])
          print(values)
      
      asyncio.run(main())
      

      这个方法,gather,为并发作业接受任意数量的参数而不是一个列表,所以我们解包。

      在我的例子中需要这个中间值 values 是很常见的,而不是设计你的函数/方法来产生副作用。

      【讨论】:

        猜你喜欢
        • 2021-03-18
        • 2021-07-22
        • 2020-06-30
        • 2020-04-28
        • 2020-11-01
        • 2019-06-22
        • 2021-03-21
        • 2018-10-27
        • 2020-04-25
        相关资源
        最近更新 更多