Ошибка в вашем коде в том, что вы звоните time.sleep
. Вы никогда не должны вызывать эту функцию в асинхронном коде, она блокирует весь цикл событий;await asyncio.sleep()
вместо. С точки зрения JavaScript, вызов time.sleep
почти так же плох, как и сон как этот вместо как этот . (Я говорю «почти», потому что time.sleep
по крайней мере не сжигает циклы ЦП во время ожидания.)
Попытки обойти эту ошибку привели ко второй проблеме, использованию asyncio.sleep(0)
для предоставления контроляцикл событий. Хотя идиома была добавлена рано, поведение было задокументировано только значительно позже . Как Guido намекает в исходном выпуске, явная уступка в цикле событий подходит только для расширенного использования, и его использование новичками, скорее всего, является ошибкой. Если ваша длительная операция асинхронная - как в вашем коде, после замены time.sleep()
на await asyncio.sleep()
- вам не нужно переходить в цикл обработки событий вручную. Вместо этого асинхронная операция будет сбрасываться по мере необходимости на каждом await
, как это было бы в JavaScript.
В Node.js, когда мы выполняем внешний вызов API, мы получаем что-то обратно, называемое Promises, на котороммы можем дождаться окончания.
В Python future является близким аналогом, и асинхронные модели очень похожи. Одно существенное отличие состоит в том, что асинхронные функции Python возвращают не запланированные фьючерсы, а легкие сопрограммные объекты, которые вы должны либо дождаться, либо передать asyncio.create_task()
, чтобы запустить их. Поскольку ваш код выполняет последнее, он выглядит правильно.
Возвращаемое значение create_task
равно объекту , который реализует интерфейс Future
. Будущие виды спорта - метод add_done_callback
с семантикой, которую вы ожидаете. Но гораздо лучше вместо этого просто await
будущее - это делает код более читабельным, и становится ясно, куда идут исключения.
Кроме того, вы, вероятно, хотите скорее использовать asyncio.gather()
чем asyncio.wait()
, чтобы исключения не остались незамеченными. Если вы используете Python 3.7, попробуйте использовать asyncio.run()
для запуска основной функции async.