结合 Promise.all 等可等待对象

问题描述 投票:0回答:5

在异步 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
5个回答
106
投票

相当于使用

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

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

因为当您

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

编辑

原来的答案使用了较低级别的

asyncio.wait


23
投票

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

正如文档所示,asyncio.gather() 方法的结果值的顺序对应于 aws 中可等待的顺序。然而,asyncio.wait() 的结果值的顺序不会做同样的事情。你可以测试一下。


16
投票

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,为并发作业采用任意数量的参数而不是列表,所以我们解压。

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

    


3
投票
Python 3.11

起: 同时创建和运行任务并等待其完成的更现代方法是

asyncio.TaskGroup

...尽管如此,我找不到任何理由说明为什么 TaskGroup 应该优先于 Gather


0
投票
asyncio.as_completed()

创建可用于 values-循环的协程(awaitables)迭代器:

for

应用于您的代码时:

async def asyncio_all(aws): for future in asyncio.as_completed(aws) await future

import asyncio

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

async def aio_all(seq):
  for f in asyncio.as_completed(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()
这最接近 JavaScript 
started 1 started 0 started 2 started 6 started 8 started 7 started 5 started 9 started 4 started 3 finished 1 finished 0 finished 2 finished 6 finished 8 finished 7 finished 5 finished 9 finished 4 finished 3

。然而,使用

Promise.all
asyncio.gather
(从 v3.11 开始)似乎是更 Pythonic 的方式。
    

© www.soinside.com 2019 - 2024. All rights reserved.