在异步 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()
但它按顺序执行我的任务。
等待多个可等待项的最简单方法是什么? 为什么我的方法不起作用?
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
。
我注意到,如果我们想要有序的结果,asyncio.gather()可能是比 asyncio.wait() 更好的等待方法。
正如文档所示,asyncio.gather() 方法的结果值的顺序对应于 aws 中可等待的顺序。然而,asyncio.wait() 的结果值的顺序不会做同样的事情。你可以测试一下。
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
-循环的协程(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 的方式。