В разделе рассмотрены методы низкоуровнего API цикла событий, позволяющие планировать обратные вызовы в асинхронном коде с применением модуля asyncio
.
Прежде чем что-то делать с циклом событий, его необходимо создать или получить функциями, описанными в разделе "Создание, запуск и получение цикла событий".
loop.call_soon()
планирует обратный вызов,loop.call_soon_threadsafe()
потокобезопасный обратный вызов,loop.call_later()
планирует обратный вызов с задержкой,loop.call_at()
планирует обратный вызов в определенное время,loop.time()
возвращает текущее время,asyncio.Handle
- дескриптор обратного вызова,loop.call_later()
.loop.call_soon(callback, *args, context=None)
:Метод loop.call_soon()
планирует обратный вызов callback
с аргументами args
на следующей итерации цикла событий.
Обратные вызовы вызываются в том порядке, в котором они зарегистрированы. Каждый обратный вызов будет вызываться ровно один раз.
Необязательный ключевой аргумент context
, позволяет указать настраиваемый contextvars.Context
для выполнения обратного вызова. Если аргумент context
не предоставлен, то используется текущий контекст.
Возвращается экземпляр asyncio.Handle
, который позже можно использовать для отмены обратного вызова.
Этот метод не является потокобезопасным.
loop.call_soon_threadsafe(callback, *args, context=None)
:Метод loop.call_soon_threadsafe()
потокобезопасный вариант метода loop.call_soon()
. Должен использоваться для планирования обратных вызовов из другого потока.
Подробнее смотрите в разделе "Параллелизм и многопоточность в модуле asyncio
".
Изменено в Python 3.7: добавлен ключевой аргумент
context
.
Примечание. Большинство функций планирования asyncio
не позволяют передавать ключевые аргументы. Что бы иметь такую возможность используйте функцию functools.partial()
:
# планирует вызов `print('Hello', flush=True)` loop.call_soon( functools.partial(print, "Hello", flush=True))
Использование partial
объектов обычно более удобно, чем использование лямбда-выражений, поскольку asyncio
лучше отображает partial
объекты в сообщениях об отладке и ошибках.
Цикл событий предоставляет механизмы для планирования функций обратного вызова, которые будут вызываться в какой-то момент в будущем. Цикл событий использует монотонные часы для отслеживания времени.
loop.call_later(delay, callback, *args, context=None)
:Метод loop.call_later()
планирует обратный вызов callback
, который будет вызываться после заданного количества секунд задержки delay
Аргумент delay
может быть либо int
, либо float
.
Возвращается экземпляр asyncio.TimerHandle
, который можно использовать для отмены обратного вызова.
Обратный вызов будет вызван ровно один раз. Если на одно и то же время запланированы два обратных вызова, то порядок их вызова не определен.
Необязательные позиционные аргументы будут переданы обратному вызову при его вызове. Если необходимо, чтобы обратный вызов вызывался с ключевыми аргументами, то используйте functools.partial()
.
Необязательный ключевой аргумент context
, позволяет указать настраиваемый contextvars.Context
для выполнения обратного вызова. Когда контекст не предоставлен, то используется текущий контекст.
Изменено в Python 3.7: добавлен параметр
context
.Изменено в Python 3.8: в Python 3.7 и ранее с реализацией цикла событий по умолчанию задержка
delay
не могла превышать одного дня. Это было исправлено в Python 3.8.
loop.call_at(when, callback, *args, context=None)
:Метод loop.call_at()
планирует обратный вызов callback
, который будет вызываться в заданную абсолютную временную метку when
, используя ту же ссылку времени, что и метод loop.time()
.
Аргумент when
может быть либо int
, либо float
. Поведение этого метода такое же, как и `loop.call_later().
Возвращается экземпляр asyncio.TimerHandle
, который можно использовать для отмены обратного вызова.
Изменено в Python 3.7: добавлен параметр
context
.Изменено в Python 3.8: в Python 3.7 и ранее с реализацией цикла событий по умолчанию задержка
delay
не могла превышать одного дня. Это было исправлено в Python 3.8.
loop.time()
:Метод loop.time()
возвращает текущее время в виде значения float
в соответствии с внутренними монотонными часами цикла событий.
Изменено в Python 3.8: в Python 3.7 и ранее с реализацией цикла событий по умолчанию задержка
delay
не могла превышать одного дня. Это было исправлено в Python 3.8.
Смотрите также функцию asyncio.sleep()
.
asyncio.Handle
:Класс asyncio.Handle
представляет собой объект-оболочку обратного вызова и имеет методы.
Handle.get_context()
:Новое в Python 3.12.
Метод Handle.get_context()
возвращает объект contextvars.Context
, связанный с дескриптором.
Handle.cancel()
:Метод Handle.cancel()
отменяет обратный вызов. Если обратный вызов уже был отменен или выполнен, то этот метод не действует.
Handle.cancelled()
:Метод Handle.cancel()
возвращает True
, если обратный вызов был отменен.
Новое в Python 3.7.
asyncio.TimerHandle
:Класс asyncio.TimerHandle
является подклассом asyncio.Handle
, представляет собой объект-оболочку обратного вызова и имеет один метод.
TimerHandle.when()
:Метод TimerHandle.when()
возвращает запланированное время обратного вызова в виде секунд float
.
Время - это абсолютная временная метка, использующая ту же ссылку на время, что и loop.time()
.
Новое в Python 3.7.
import asyncio def mark_done(future, result): print(f'Установка будущего результата: {result!r}') future.set_result(result) async def main(loop): future = loop.create_future() print('Планирование `mark_done()`') loop.call_soon(mark_done, future, 'RESULT') print('Приостановка работы сопрограммы') result = await future print(f'Получение результата `future`: {future.result()!r}') return result if __name__ == '__main__': loop = asyncio.get_event_loop() try: print('Вход в цикл событий') result = loop.run_until_complete(main(loop)) print(f'Результат: {result!r}') finally: loop.close() # Вход в цикл событий # Планирование `mark_done()` # Приостановка работы сопрограммы # Установка будущего результата: 'RESULT' # Получение результата `future`: 'RESULT' # Результат: 'RESULT'
loop.call_later()
.Пример обратного вызова, отображающего текущее время каждую секунду. Обратный вызов использует метод loop.call_later()
для перепланирования себя через 5 секунд, а затем останавливает цикл событий.
import asyncio import datetime def display_date(end_time, loop): print(datetime.datetime.now().strftime('%H:%M:%S')) if (loop.time() + 1.0) < end_time: loop.call_later(1, display_date, end_time, loop) else: loop.stop() loop = asyncio.get_event_loop() # Планирование первого вызова display_date() end_time = loop.time() + 5.0 loop.call_soon(display_date, end_time, loop) # Блокировка вызова прерывается loop.stop() try: loop.run_forever() finally: loop.close() # 08:58:23 # 08:58:24 # 08:58:25 # 08:58:26 # 08:58:27