return_when=FIRST_COMPLETED
не гарантирует, что будет выполнена только одна задача. Это гарантирует, что ожидание завершится, как только задача завершится, но вполне возможно, что другие задачи завершатся «в одно и то же время», что для asyncio означает одну и ту же итерацию цикла событий. Рассмотрим, например, следующий код:
async def noop():
pass
async def main():
done, pending = await asyncio.wait(
[noop(), noop(), noop()], return_when=asyncio.FIRST_COMPLETED)
print(len(done), len(pending))
asyncio.run(main())
Это печатает 3 0
, так же, как ваш код. Почему?
asyncio.wait
делает две вещи: отправляет сопрограммы в цикл обработки событий и устанавливает обратные вызовы, чтобы уведомить его о завершении любого из них. Однако сопрограмма noop
не содержит await
, поэтому ни один из вызовов noop()
не приостанавливается, каждый просто делает свое дело и немедленно возвращается. В результате все три экземпляра сопрограммы заканчиваются в одном и том же проходе цикла событий. wait
затем сообщают, что все три сопрограммы закончились, о чем должным образом сообщается.
Если вы измените noop
на ожидание случайного сна, например, измените pass
на await asyncio.sleep(0.1 * random.random())
, вы получите ожидаемое поведение. С await
сопрограммы больше не завершаются в одно и то же время, и wait
сообщит о первом, как только он его обнаружит.
Это показывает истинную причину проблемы с вашим кодом: _perform_query
не ждет . Это указывает на то, что вы не используете асинхронную базовую библиотеку или используете ее неправильно. Вызов SearchSubtitles
, скорее всего, просто блокирует цикл обработки событий, который , по-видимому, работает в тривиальных тестах, но нарушает важные асинхронные функции, такие как одновременное выполнение задач.