Как в Python asyncio запустить несколько корутин конкурентно и дождаться их завершения

«Как в Python asyncio запустить несколько корутин конкурентно и дождаться их завершения» — вопрос из категории Асинхронность, который задают на 10% собеседований Python Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Для конкурентного запуска нескольких корутин и сбора их результатов используется высокоуровневая функция asyncio.gather().

Она принимает на вход одну или несколько корутин, планирует их выполнение в цикле событий и возвращает Future, который завершается, когда все переданные корутины отработали. Результатом является список возвращаемых значений корутин в том же порядке, в котором они были переданы.

Ключевые особенности:

  • Простота использования: Идеально подходит для сценария "запустить всё и дождаться всех".
  • Сбор результатов: Автоматически собирает и упорядочивает результаты.
  • Обработка ошибок: По умолчанию, если одна из корутин вызывает исключение, gather() немедленно прерывается и пробрасывает это исключение. Поведение можно изменить, установив флаг return_exceptions=True, тогда исключения будут возвращены в списке результатов наравне с успешными значениями.

Пример:

import asyncio

async def fetch_data(source: str, delay: int) -> str:
    print(f"Начинаю загрузку из {source}...")
    await asyncio.sleep(delay)
    result = f"Данные из {source}"
    print(f"Загрузка из {source} завершена.")
    return result

async def main():
    # Запускаем обе корутины конкурентно
    results = await asyncio.gather(
        fetch_data("API", 2),
        fetch_data("DB", 1)
    )
    print(f"nВсе результаты: {results}")

# Вывод покажет, что DB завершит работу раньше, 
# но gather дождется завершения API.
# >> Начинаю загрузку из API...
# >> Начинаю загрузку из DB...
# >> Загрузка из DB завершена.
# >> Загрузка из API завершена.
# >> 
# >> Все результаты: ['Данные из API', 'Данные из DB']

asyncio.run(main())

В отличие от asyncio.create_task(), которая только планирует выполнение корутины и немедленно возвращает объект Task, asyncio.gather() представляет собой более удобный способ для одновременного ожидания группы задач.