Асинхронный генератор не означает, что вы выполняете итерацию одновременно!Все, что вы получаете, это больше мест для сопрограммы, чтобы уступить другим задачам.Шаги итерации по-прежнему выполняются последовательно .
Иными словами: асинхронный итератор полезен для итератора, который должен использовать ввод-вывод для получения каждого шага итерации.Подумайте, просматривая результаты веб-сокета или строк в файле.Если каждый next()
шаг по итератору требует ожидания медленного источника ввода-вывода для предоставления данных, это хороший момент для передачи управления чему-то еще, что было установлено для одновременной работы.
Если вы ожидали каждогоОтдельный шаг вашего генератора должен выполняться одновременно, тогда вам все равно придется запланировать дополнительные задачи, явно , с циклом событий.
Затем вы можете вернуться из генератора, когда все эти дополнительныезадачи выполнены.Если вы запланировали свои 4 time_consuming()
сопрограммы как задачи, используйте asyncio.wait()
, чтобы дождаться завершения одной или всех задач и получить результаты от выполненных задач, тогда да, после вашей for i in range(...):
цикл завершен, ваш процесс займет всего 4 секунды:
async def generator():
pending = []
for i in range(4, 0, -1):
pending.append(asyncio.create_task(time_consuming(i)))
while pending:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for task in done:
yield task.result()
, в этот момент вывод будет
Going to sleep for 4 seconds
Going to sleep for 3 seconds
Going to sleep for 2 seconds
Going to sleep for 1 seconds
Slept 1 seconds
Doing something with 1
Slept 2 seconds
Doing something with 2
Slept 3 seconds
Doing something with 3
Slept 4 seconds
Doing something with 4
Обратите внимание, что это реверс Порядок из ожидаемых результатов, потому что для этого нужны результаты задачи , поскольку они завершают , а не ожидают завершения создания первой задачи.Обычно это то, что вы хотите, правда.Зачем ждать 4 секунды, когда у вас уже есть готовый результат после 1?
У вас тоже может быть свой вариант, но вы просто по-другому его кодируете.Затем вы можете просто использовать asyncio.gather()
для 4 задач , который планирует запуск сопрограмм как параллельных задач, и возвращать их результаты в виде списка, после чего вы можете получить эти результаты:
async def generator():
tasks = []
for i in range(4, 0, -1):
tasks.append(time_consuming(i))
for res in await asyncio.gather(*tasks):
yield res
, но теперь вывод становится
Going to sleep for 4 seconds
Going to sleep for 3 seconds
Going to sleep for 2 seconds
Going to sleep for 1 seconds
Slept 1 seconds
Slept 2 seconds
Slept 3 seconds
Slept 4 seconds
Doing something with 4
Doing something with 3
Doing something with 2
Doing something with 1
, потому что мы не можем ничего делать до тех пор, пока самая длинная задача, time_consuming(4)
, не будет завершена, но более короткие задачи завершатся до этой точкии уже выведите свое сообщение Slept ... seconds
.