Документация Python неофициальный перевод

asyncio-task.md

964 строк · 66.7 КБ · обычная страница · сырой текст · скачать

1> **Источник:** https://python-all.ru/3.11/library/asyncio-task.html2>3> «Документация Python на русском» – неофициальный перевод официальной документации Python: версии от 2.6 до 3.16, полнотекстовый поиск, английский оригинал рядом с переводом. Эта Markdown-версия страницы предназначена для работы с LLM: вставьте её в ChatGPT, Claude или Cursor.45---67# Корутины и задачи89В этом разделе представлены высокоуровневые API asyncio для работы с корутинами и задачами.1011[Корутины](https://python-all.ru/3.11/library/asyncio-task.html#coroutines) [Ожидаемые объекты](https://python-all.ru/3.11/library/asyncio-task.html#awaitables) [Создание задач](https://python-all.ru/3.11/library/asyncio-task.html#creating-tasks) [Отмена задач](https://python-all.ru/3.11/library/asyncio-task.html#task-cancellation) [Группы задач](https://python-all.ru/3.11/library/asyncio-task.html#task-groups) [Приостановка выполнения](https://python-all.ru/3.11/library/asyncio-task.html#sleeping) [Одновременное выполнение задач](https://python-all.ru/3.11/library/asyncio-task.html#running-tasks-concurrently) [Защита от отмены](https://python-all.ru/3.11/library/asyncio-task.html#shielding-from-cancellation) [Тайм-ауты](https://python-all.ru/3.11/library/asyncio-task.html#timeouts) [Примитивы ожидания](https://python-all.ru/3.11/library/asyncio-task.html#waiting-primitives) [Выполнение в потоках](https://python-all.ru/3.11/library/asyncio-task.html#running-in-threads) [Планирование из других потоков](https://python-all.ru/3.11/library/asyncio-task.html#scheduling-from-other-threads) [Интроспекция](https://python-all.ru/3.11/library/asyncio-task.html#introspection) [Объект задачи](https://python-all.ru/3.11/library/asyncio-task.html#task-object)1213## [Корутины](https://python-all.ru/3.11/library/asyncio-task.html#id1)1415**Исходный код:** [Lib/asyncio/coroutines.py](https://python-all.ru/src/3.11/Lib/asyncio/coroutines.py)1617---1819[Корутины](https://python-all.ru/3.11/glossary.html#term-coroutine), объявленные с помощью синтаксиса async/await, являются предпочтительным способом написания приложений asyncio. Например, следующий фрагмент кода выводит «hello», ждёт 1 секунду, а затем выводит «world»:2021```python22>>> import asyncio2324>>> async def main():25...     print('hello')26...     await asyncio.sleep(1)27...     print('world')2829>>> asyncio.run(main())30hello31world32```3334Обратите внимание, что простой вызов корутины не запланирует её выполнение:3536```python37>>> main()38<coroutine object main at 0x1053bb7c8>39```4041Для непосредственного выполнения корутины asyncio предоставляет следующие механизмы:4243- Функция [`asyncio.run()`](https://python-all.ru/3.11/library/asyncio-runner.html#asyncio.run) для запуска точки входа верхнего уровня – функции «main()» (см. пример выше.)44- Ожидание корутины. Следующий фрагмент кода выведет «hello» после ожидания в 1 секунду, а затем выведет «world» после ожидания *ещё* 2 секунд:4546  ```python47  import asyncio48  import time4950  async def say_after(delay, what):51      await asyncio.sleep(delay)52      print(what)5354  async def main():55      print(f"started at {time.strftime('%X')}")5657      await say_after(1, 'hello')58      await say_after(2, 'world')5960      print(f"finished at {time.strftime('%X')}")6162  asyncio.run(main())63  ```6465  Ожидаемый вывод:6667  ```python68  started at 17:13:5269  hello70  world71  finished at 17:13:5572  ```73- Функция [`asyncio.create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task) для параллельного запуска корутин в качестве asyncio [`Tasks`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task).7475  Давайте изменим пример выше и запустим две `say_after` корутины *параллельно*:7677  ```python78  async def main():79      task1 = asyncio.create_task(80          say_after(1, 'hello'))8182      task2 = asyncio.create_task(83          say_after(2, 'world'))8485      print(f"started at {time.strftime('%X')}")8687      # Ожидать завершения обеих задач (должно занять88      # около 2 секунд).89      await task190      await task29192      print(f"finished at {time.strftime('%X')}")93  ```9495  Обратите внимание, что ожидаемый вывод теперь показывает, что фрагмент выполняется на 1 секунду быстрее, чем раньше:9697  ```python98  started at 17:14:3299  hello100  world101  finished at 17:14:34102  ```103- Класс [`asyncio.TaskGroup`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup) предоставляет более современную альтернативу [`create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task). С помощью этого API последний пример примет вид:104105  ```python106  async def main():107      async with asyncio.TaskGroup() as tg:108          task1 = tg.create_task(109              say_after(1, 'hello'))110111          task2 = tg.create_task(112              say_after(2, 'world'))113114          print(f"started at {time.strftime('%X')}")115116      # Вызов await выполняется неявно при выходе из контекстного менеджера.117118      print(f"finished at {time.strftime('%X')}")119  ```120121  Время выполнения и вывод должны быть такими же, как в предыдущей версии.122123  Новое в версии 3.11: [`asyncio.TaskGroup`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup).124125## [Ожидаемые объекты](https://python-all.ru/3.11/library/asyncio-task.html#id2)126127Говорят, что объект является **ожидаемым** объектом, если его можно использовать в выражении [`await`](https://python-all.ru/3.11/reference/expressions.html#await). Многие API asyncio предназначены для принятия ожидаемых объектов.128129Существует три основных типа *ожидаемых* объектов: **корутины**, **задачи** и **future**.130131Корутины132133Корутины Python являются *ожидаемыми объектами*, а значит, их можно ожидать из других корутин:134135```python136import asyncio137138async def nested():139    return 42140141async def main():142    # Если просто вызвать "nested()", ничего не произойдет.143    # Создается объект корутины, но он не ожидается,144    # поэтому он *вообще не выполнится*.145    nested()146147    # Давайте поступим иначе и теперь ожидаем его:148    print(await nested())  # выведет "42".149150asyncio.run(main())151```152153> **Важно**154>155> В данной документации термин «корутина» может использоваться для двух тесно связанных понятий:156>157> - *корутинная функция*: функция [`async def`](https://python-all.ru/3.11/reference/compound_stmts.html#async-def);158> - *объект корутины*: объект, возвращаемый при вызове *корутинной функции*.159160Задачи161162*Задачи* используются для планирования корутин *параллельно*.163164Когда корутина обёрнута в *задачу* с помощью таких функций, как [`asyncio.create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task), корутина автоматически планируется к скорому выполнению:165166```python167import asyncio168169async def nested():170    return 42171172async def main():173    # Запланировать выполнение nested() вскоре конкурентно174    # с "main()".175    task = asyncio.create_task(nested())176177    # "задача" теперь можно использовать для отмены "nested()" или178    # можно просто применить await, чтобы дождаться завершения:179    await task180181asyncio.run(main())182```183184Future185186[`Future`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future) – это специальный **низкоуровневый** ожидаемый объект, который представляет **конечный результат** асинхронной операции.187188Когда объект Future *ожидается*, это означает, что корутина будет ожидать, пока Future не будет разрешён в каком-то другом месте.189190Объекты Future в asyncio необходимы для того, чтобы код, основанный на колбэках, мог использоваться с async/await.191192Обычно **нет необходимости** создавать объекты Future в коде уровня приложения.193194Объекты Future, иногда предоставляемые библиотеками и некоторыми API asyncio, можно ожидать (await):195196```python197async def main():198    await function_that_returns_a_future_object()199200    # это также допустимо:201    await asyncio.gather(202        function_that_returns_a_future_object(),203        some_python_coroutine()204    )205```206207Хороший пример низкоуровневой функции, возвращающей объект Future – [`loop.run_in_executor()`](https://python-all.ru/3.11/library/asyncio-eventloop.html#asyncio.loop.run_in_executor).208209## [Создание задач](https://python-all.ru/3.11/library/asyncio-task.html#id3)210211**Исходный код:** [Lib/asyncio/tasks.py](https://python-all.ru/src/3.11/Lib/asyncio/tasks.py)212213---214215#### `asyncio.create_task(coro, *, name=None, context=None)`216217Wrap the *coro* [coroutine](https://python-all.ru/3.11/library/asyncio-task.html#coroutine) into a [`Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task) and schedule its execution. Return the Task object.218219Если *name* не равен `None`, он устанавливается как имя задачи с помощью [`Task.set_name()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.set_name).220221Необязательный именованный аргумент *context* позволяет указать пользовательский [`contextvars.Context`](https://python-all.ru/3.11/library/contextvars.html#contextvars.Context) для выполнения *coro*. Копия текущего контекста создаётся, если *context* не передан.222223Задача выполняется в цикле, возвращаемом [`get_running_loop()`](https://python-all.ru/3.11/library/asyncio-eventloop.html#asyncio.get_running_loop); [`RuntimeError`](https://python-all.ru/3.11/library/exceptions.html#RuntimeError) возбуждается, если в текущем потоке нет запущенного цикла.224225> **Примечание**226>227> [`asyncio.TaskGroup.create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup.create_task) – это более новая альтернатива, позволяющая удобно ожидать выполнения группы связанных задач.228229> **Важно**230>231> Сохраняйте ссылку на результат этой функции, чтобы задача не исчезла во время выполнения. Цикл событий хранит только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть собрана сборщиком мусора в любой момент, даже до завершения. Для надёжных фоновых задач «запустил и забыл» собирайте их в коллекцию:232>233> ```python234> background_tasks = set()235>236> for i in range(10):237>     task = asyncio.create_task(some_coro(param=i))238>239>     # Добавить задачу в множество. Это создаёт сильную ссылку.240>     background_tasks.add(task)241>242>     # Чтобы предотвратить постоянное хранение ссылок на завершённые задачи,243>     # нужно, чтобы каждая задача удаляла свою собственную ссылку из множества после244>     # завершения:245>     task.add_done_callback(background_tasks.discard)246> ```247248Добавлено в версии 3.7.249250Изменено в версии 3.8: Добавлен параметр *name*.251252Изменено в версии 3.11: Добавлен параметр *context*.253254## [Отмена задачи](https://python-all.ru/3.11/library/asyncio-task.html#id4)255256Задачи можно легко и безопасно отменять. При отмене задачи в ней при первой же возможности будет возбуждено [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError).257258Рекомендуется, чтобы корутины использовали блоки `try/finally` для надёжного выполнения логики очистки. Если [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) явно перехватывается, его обычно следует пробрасывать дальше после завершения очистки. [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) напрямую наследует [`BaseException`](https://python-all.ru/3.11/library/exceptions.html#BaseException), поэтому большинству кода не нужно знать о нём.259260Компоненты asyncio, обеспечивающие структурную конкурентность, такие как [`asyncio.TaskGroup`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup) и [`asyncio.timeout()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.timeout), внутренне реализованы с использованием отмены и могут работать некорректно, если корутина подавляет [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError). Аналогично, пользовательскому коду обычно не следует вызывать [`uncancel`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel). Однако в случаях, когда подавление [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) действительно необходимо, нужно также вызвать `uncancel()`, чтобы полностью снять состояние отмены.261262## [Группы задач](https://python-all.ru/3.11/library/asyncio-task.html#id5)263264Группы задач объединяют API создания задач с удобным и надёжным способом ожидания завершения всех задач в группе.265266#### `class asyncio.TaskGroup`267268An [asynchronous context manager](https://python-all.ru/3.11/reference/datamodel.html#async-context-managers) holding a group of tasks. Tasks can be added to the group using [`create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task). All tasks are awaited when the context manager exits.269270Новое в версии 3.11.271272#### `create_task(coro, *, name=None, context=None)`273274Создаёт задачу в этой группе задач. Сигнатура совпадает с сигнатурой [`asyncio.create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task).275276Пример:277278```python279async def main():280    async with asyncio.TaskGroup() as tg:281        task1 = tg.create_task(some_coro(...))282        task2 = tg.create_task(another_coro(...))283    print("Both tasks have completed now.")284```285286Оператор `async with` будет ждать завершения всех задач в группе. Во время ожидания в группу могут добавляться новые задачи (например, если передать `tg` в одну из корутин и вызвать `tg.create_task()` в этой корутине). После того как последняя задача завершится и блок `async with` будет завершён, новые задачи добавлять в группу будет нельзя.287288Когда одна из задач, принадлежащих группе, впервые завершается с ошибкой с исключением, отличным от [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError), оставшиеся задачи в группе отменяются. После этого в группу нельзя добавлять новые задачи. Если в этот момент тело оператора `async with` всё ещё активно (т.е. [`__aexit__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__aexit__) ещё не был вызван), то задача, непосредственно содержащая оператор `async with`, также отменяется. Возникшее [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) прервёт `await`, но не всплывёт за пределы содержащего оператора `async with`.289290Когда все задачи завершены, если какие-либо задачи завершились ошибкой с исключением, отличным от [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError), эти исключения объединяются в [`ExceptionGroup`](https://python-all.ru/3.11/library/exceptions.html#ExceptionGroup) или [`BaseExceptionGroup`](https://python-all.ru/3.11/library/exceptions.html#BaseExceptionGroup) (в зависимости от ситуации; смотрите их документацию), которое затем возбуждается.291292Два базовых исключения обрабатываются особым образом: Если какая-либо задача завершается ошибкой с [`KeyboardInterrupt`](https://python-all.ru/3.11/library/exceptions.html#KeyboardInterrupt) или [`SystemExit`](https://python-all.ru/3.11/library/exceptions.html#SystemExit), группа задач всё равно отменяет оставшиеся задачи и ожидает их, но затем первоначальное [`KeyboardInterrupt`](https://python-all.ru/3.11/library/exceptions.html#KeyboardInterrupt) или [`SystemExit`](https://python-all.ru/3.11/library/exceptions.html#SystemExit) возбуждается повторно вместо [`ExceptionGroup`](https://python-all.ru/3.11/library/exceptions.html#ExceptionGroup) или [`BaseExceptionGroup`](https://python-all.ru/3.11/library/exceptions.html#BaseExceptionGroup).293294Если тело оператора `async with` завершается исключением (т.е. [`__aexit__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__aexit__) вызывается с установленным исключением), это обрабатывается так же, как если бы одна из задач завершилась ошибкой: оставшиеся задачи отменяются и ожидаются, а исключения, не связанные с отменой, группируются в gруппу исключений и возбуждаются. Исключение, переданное в [`__aexit__()`](https://python-all.ru/3.11/reference/datamodel.html#object.__aexit__), если только оно не является [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError), также включается в группу исключений. Тот же особый случай применяется для [`KeyboardInterrupt`](https://python-all.ru/3.11/library/exceptions.html#KeyboardInterrupt) и [`SystemExit`](https://python-all.ru/3.11/library/exceptions.html#SystemExit), как в предыдущем абзаце.295296## [Ожидание](https://python-all.ru/3.11/library/asyncio-task.html#id6)297298#### `coroutine asyncio.sleep(delay, result=None)`299300Приостанавливает выполнение на *delay* секунд.301302Если *result* указан, он возвращается вызывающей стороне после завершения корутины.303304`sleep()` всегда приостанавливает текущую задачу, позволяя выполняться другим задачам.305306Установка задержки в 0 обеспечивает оптимизированный путь, позволяющий выполняться другим задачам. Это может использоваться длительными функциями, чтобы избежать блокировки цикла событий на всё время вызова функции.307308Пример корутины, выводящей текущую дату каждую секунду в течение 5 секунд:309310```python311import asyncio312import datetime313314async def display_date():315    loop = asyncio.get_running_loop()316    end_time = loop.time() + 5.0317    while True:318        print(datetime.datetime.now())319        if (loop.time() + 1.0) >= end_time:320            break321        await asyncio.sleep(1)322323asyncio.run(display_date())324```325326Изменено в версии 3.10: Удалён параметр *loop*.327328## [Одновременный запуск задач](https://python-all.ru/3.11/library/asyncio-task.html#id7)329330#### `awaitable asyncio.gather(*aws, return_exceptions=False)`331332Выполняет [ожидаемые объекты](https://python-all.ru/3.11/library/asyncio-task.html#asyncio-awaitables) из последовательности *aws* *одновременно*.333334Если любой ожидаемый объект из *aws* является корутиной, он автоматически планируется как задача.335336Если все ожидаемые объекты завершены успешно, результатом является сводный список возвращённых значений. Порядок значений результата соответствует порядку ожидаемых объектов в *aws*.337338Если *return\_exceptions* равно `False` (по умолчанию), первое возбуждённое исключение немедленно распространяется на задачу, ожидающую `gather()`. Остальные ожидаемые объекты в последовательности *aws* **не будут отменены** и продолжат выполнение.339340Если *return\_exceptions* равно `True`, исключения обрабатываются так же, как успешные результаты, и объединяются в списке результатов.341342Если `gather()` *отменён*, все переданные ожидаемые объекты (которые ещё не завершились) также *отменяются*.343344Если любая задача или Future из последовательности *aws* *отменена*, это рассматривается так, как если бы она возбудила [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) – вызов `gather()` в этом случае **не** отменяется. Это сделано для предотвращения ситуации, когда отмена одной переданной задачи или Future приводит к отмене других задач или Future.345346> **Примечание**347>348> Более современный способ создания и одновременного выполнения задач, а также ожидания их завершения – это [`asyncio.TaskGroup`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup).349350Пример:351352```python353import asyncio354355async def factorial(name, number):356    f = 1357    for i in range(2, number + 1):358        print(f"Task {name}: Compute factorial({number}), currently i={i}...")359        await asyncio.sleep(1)360        f *= i361    print(f"Task {name}: factorial({number}) = {f}")362    return f363364async def main():365    # Запланировать три вызова *конкурентно*:366    L = await asyncio.gather(367        factorial("A", 2),368        factorial("B", 3),369        factorial("C", 4),370    )371    print(L)372373asyncio.run(main())374375# Ожидаемый вывод:376#377#     Задача A: вычисление factorial(2), сейчас i=2...378#     Задача B: вычисление factorial(3), сейчас i=2...379#     Задача C: вычисление factorial(4), сейчас i=2...380#     Задача A: factorial(2) = 2381#     Задача B: вычисление factorial(3), сейчас i=3...382#     Задача C: вычисление factorial(4), сейчас i=3...383#     Задача B: factorial(3) = 6384#     Задача C: вычисление factorial(4), сейчас i=4...385#     Задача C: factorial(4) = 24386#     [2, 6, 24]387```388389> **Примечание**390>391> Если *return\_exceptions* равно False, отмена gather() после того, как он был помечен как завершённый, не отменит ни один из переданных ожидаемых объектов. Например, gather может быть помечен как завершённый после распространения исключения вызывающему коду; следовательно, вызов `gather.cancel()` после перехвата исключения (возбуждённого одним из ожидаемых объектов) из gather не отменит другие ожидаемые объекты.392393Изменено в версии 3.7: Если сам *gather* отменён, отмена распространяется независимо от *return\_exceptions*.394395Изменено в версии 3.10: Удалён параметр *loop*.396397Устарело с версии 3.10: Предупреждение об устаревании выдается, если не предоставлены позиционные аргументы или не все позиционные аргументы являются объектами, подобными Future, и нет запущенного цикла событий.398399## [Защита от отмены](https://python-all.ru/3.11/library/asyncio-task.html#id8)400401#### `awaitable asyncio.shield(aw)`402403Защищает [ожидаемый объект](https://python-all.ru/3.11/library/asyncio-task.html#asyncio-awaitables) от [`cancelled`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel).404405Если *aw* является корутиной, она автоматически планируется как задача.406407Выражение:408409```python410task = asyncio.create_task(something())411res = await shield(task)412```413414эквивалентно:415416```python417res = await something()418```419420*За исключением* того, что если содержащая его корутина отменяется, задача, выполняющаяся в `something()`, не отменяется. С точки зрения `something()` отмена не произошла. Хотя её вызывающий код всё ещё отменён, поэтому выражение «await» по-прежнему возбуждает [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError).421422Если `something()` отменяется другими средствами (т.е. изнутри самой себя), это также отменит `shield()`.423424Если требуется полностью игнорировать отмену (не рекомендуется), функцию `shield()` следует комбинировать с конструкцией try/except следующим образом:425426```python427task = asyncio.create_task(something())428try:429    res = await shield(task)430except CancelledError:431    res = None432```433434> **Важно**435>436> Сохраняйте ссылку на задачи, переданные этой функции, чтобы избежать исчезновения задачи во время выполнения. Цикл событий хранит только слабые ссылки на задачи. Задача, на которую нет других ссылок, может быть собрана сборщиком мусора в любой момент, даже до её завершения.437438Изменено в версии 3.10: Удалён параметр *loop*.439440Устарело с версии 3.10: Предупреждение об устаревании выдаётся, если *aw* не является объектом, подобным Future, и нет запущенного цикла событий.441442## [Тайм-ауты](https://python-all.ru/3.11/library/asyncio-task.html#id9)443444#### `asyncio.timeout(delay)`445446Возвращает [асинхронный менеджер контекста](https://python-all.ru/3.11/reference/datamodel.html#async-context-managers), который можно использовать для ограничения времени ожидания чего-либо.447448*delay* может быть `None` или числом с плавающей точкой или целым числом секунд ожидания. Если *delay* равен `None`, ограничение по времени не применяется; это может быть полезно, если delay неизвестен на момент создания менеджера контекста.449450В любом случае, менеджер контекста можно перенастроить после создания с помощью [`Timeout.reschedule()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Timeout.reschedule).451452Пример:453454```python455async def main():456    async with asyncio.timeout(10):457        await long_running_task()458```459460Если выполнение `long_running_task` занимает более 10 секунд, менеджер контекста отменяет текущую задачу и обрабатывает возникшее [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) внутренне, преобразуя его в [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError), которое можно перехватить и обработать.461462> **Примечание**463>464> Менеджер контекста [`asyncio.timeout()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.timeout) преобразует [`asyncio.CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) в [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError), что означает, что [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError) может быть перехвачено только *за пределами* менеджера контекста.465466Пример перехвата [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError):467468```python469async def main():470    try:471        async with asyncio.timeout(10):472            await long_running_task()473    except TimeoutError:474        print("The long operation timed out, but we've handled it.")475476    print("This statement will run regardless.")477```478479Контекстный менеджер, получаемый с помощью [`asyncio.timeout()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.timeout), может быть перенесён на другой срок и проверен.480481#### `class asyncio.Timeout(when)`482483[Асинхронный контекстный менеджер](https://python-all.ru/3.11/reference/datamodel.html#async-context-managers) для отмены просроченных корутин.484485`when` должно быть абсолютным моментом времени, когда контекст должен сработать по таймауту, измеряемым по часам цикла событий:486487- Если `when` равно `None`, таймаут никогда не сработает.488- Если `when < loop.time()`, таймаут сработает на следующей итерации цикла событий.489490> #### `when() → float | None`491>492> Возвращает текущий срок, или `None`, если текущий срок не задан.493>494> #### `reschedule(when: float | None)`495>496> Перенести таймаут.497>498> #### `expired() → bool`499>500> Возвращает, превысил ли контекстный менеджер свой срок (истек).501502Пример:503504```python505async def main():506    try:507        # В начале мы не знаем тайм-аут, поэтому передаём ``None``.508        async with asyncio.timeout(None) as cm:509            # Теперь мы знаем тайм-аут, поэтому перепланируем его.510            new_deadline = get_running_loop().time() + 10511            cm.reschedule(new_deadline)512513            await long_running_task()514    except TimeoutError:515        pass516517    if cm.expired():518        print("Looks like we haven't finished on time.")519```520521Контекстные менеджеры таймаута можно безопасно вкладывать друг в друга.522523Новое в версии 3.11.524525#### `asyncio.timeout_at(when)`526527Похоже на [`asyncio.timeout()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.timeout), за исключением того, что *when* – это абсолютное время остановки ожидания, или `None`.528529Пример:530531```python532async def main():533    loop = get_running_loop()534    deadline = loop.time() + 20535    try:536        async with asyncio.timeout_at(deadline):537            await long_running_task()538    except TimeoutError:539        print("The long operation timed out, but we've handled it.")540541    print("This statement will run regardless.")542```543544Новое в версии 3.11.545546#### `coroutine asyncio.wait_for(aw, timeout)`547548Ожидает завершения *aw* [ожидаемого объекта](https://python-all.ru/3.11/library/asyncio-task.html#asyncio-awaitables) с таймаутом.549550Если *aw* является корутиной, она автоматически планируется как задача.551552*timeout* может быть либо `None`, либо числом с плавающей запятой или целым числом секунд ожидания. Если *timeout* равно `None`, блокирует выполнение до завершения future.553554Если происходит таймаут, он отменяет задачу и возбуждает [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError).555556Чтобы избежать задачи [`cancellation`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel), оберните её в [`shield()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.shield).557558Функция будет ждать, пока future не будет фактически отменена, поэтому общее время ожидания может превысить *timeout*. Если во время отмены произойдёт исключение, оно будет распространено.559560Если ожидание отменяется, future *aw* также отменяется.561562Пример:563564```python565async def eternity():566    # Спать в течение часа567    await asyncio.sleep(3600)568    print('yay!')569570async def main():571    # Ждать не более 1 секунды572    try:573        await asyncio.wait_for(eternity(), timeout=1.0)574    except TimeoutError:575        print('timeout!')576577asyncio.run(main())578579# Ожидаемый вывод:580#581#     тайм-аут!582```583584Изменено в версии 3.7: Когда *aw* отменяется из-за таймаута, `wait_for` ожидает отмены *aw*. Ранее оно немедленно вызывало [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError).585586Изменено в версии 3.10: Удалён параметр *loop*.587588Изменено в версии 3.11: Возбуждает [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError) вместо [`asyncio.TimeoutError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.TimeoutError).589590## [Примитивы ожидания](https://python-all.ru/3.11/library/asyncio-task.html#id10)591592#### `coroutine asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)`593594Выполняет экземпляры [`Future`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future) и [`Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task) из итерируемого объекта *aws* параллельно и блокирует выполнение до наступления условия, указанного параметром *return\_when*.595596Итерируемый объект *aws* не должен быть пустым, а генераторы, порождающие задачи, не принимаются.597598Возвращает два набора задач/future: `(done, pending)`.599600Использование:601602```python603done, pending = await asyncio.wait(aws)604```605606*timeout* (число с плавающей точкой или целое) позволяет указать максимальное время ожидания в секундах перед возвратом результата.607608Обратите внимание, что эта функция не вызывает [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError). Объекты Future или задачи, не завершённые к моменту таймаута, просто возвращаются во втором наборе.609610*return\_when* указывает, когда эта функция должна вернуть результат. Это должна быть одна из следующих констант:611612| Константа | Описание |613| --- | --- |614| asyncio.FIRST\_COMPLETED | Функция возвращает результат, когда любой future завершается или отменяется. |615| asyncio.FIRST\_EXCEPTION | Функция вернёт результат, когда любой future завершится, возбудив исключение. Если ни один future не возбудит исключение, то это эквивалентно [`ALL_COMPLETED`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.ALL_COMPLETED). |616| asyncio.ALL\_COMPLETED | Функция вернёт результат, когда все futures завершатся или будут отменены. |617618В отличие от [`wait_for()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.wait_for), `wait()` не отменяет futures при возникновении таймаута.619620Изменено в версии 3.10: Удалён параметр *loop*.621622Изменено в версии 3.11: Запрещена прямая передача объектов корутин в `wait()`.623624#### `asyncio.as_completed(aws, *, timeout=None)`625626Запустить [ожидаемые объекты](https://python-all.ru/3.11/library/asyncio-task.html#asyncio-awaitables) из итерируемого объекта *aws* одновременно. Генераторы, порождающие задачи, не принимаются в качестве итерируемого *aws*. Возвращает итератор корутин. Каждую возвращённую корутину можно ожидать, чтобы получить самый ранний следующий результат из итерируемого объекта оставшихся ожидаемых объектов.627628Вызывает [`TimeoutError`](https://python-all.ru/3.11/library/exceptions.html#TimeoutError), если время ожидания истекает до того, как все Future будут завершены.629630Пример:631632```python633for coro in as_completed(aws):634    earliest_result = await coro635    # ...636```637638Изменено в версии 3.10: Удалён параметр *loop*.639640Устарело с версии 3.10: Выдаётся предупреждение об устаревании, если не все ожидаемые объекты в итерируемом *aws* являются объектами, подобными Future, и отсутствует запущенный цикл событий.641642## [Запуск в потоках](https://python-all.ru/3.11/library/asyncio-task.html#id11)643644#### `coroutine asyncio.to_thread(func, /, *args, **kwargs)`645646Асинхронно запускает функцию *func* в отдельном потоке.647648Любые \*args и \*\*kwargs, переданные в эту функцию, напрямую передаются в *func*. Также распространяется текущий [`contextvars.Context`](https://python-all.ru/3.11/library/contextvars.html#contextvars.Context), что позволяет обращаться к контекстным переменным из потока цикла событий в отдельном потоке.649650Возвращает корутину, которую можно ожидать для получения конечного результата *func*.651652Эта корутинная функция в первую очередь предназначена для выполнения функций/методов, связанных с вводом-выводом, которые в противном случае заблокировали бы цикл событий, если бы выполнялись в главном потоке. Например:653654```python655def blocking_io():656    print(f"start blocking_io at {time.strftime('%X')}")657    # Обратите внимание, что time.sleep() можно заменить любой блокирующей658    # операцией ввода-вывода, например файловыми операциями.659    time.sleep(1)660    print(f"blocking_io complete at {time.strftime('%X')}")661662async def main():663    print(f"started main at {time.strftime('%X')}")664665    await asyncio.gather(666        asyncio.to_thread(blocking_io),667        asyncio.sleep(1))668669    print(f"finished main at {time.strftime('%X')}")670671asyncio.run(main())672673# Ожидаемый вывод:674#675# запущен main в 19:50:53676# запуск blocking_io в 19:50:53677# blocking_io завершён в 19:50:54678# main завершён в 19:50:54679```680681Прямой вызов `blocking_io()` в любой корутине заблокирует цикл событий на всё время выполнения, добавив 1 секунду ко времени работы. Вместо этого, используя `asyncio.to_thread()`, можно выполнить его в отдельном потоке без блокировки цикла событий.682683> **Примечание**684>685> Из-за [GIL](https://python-all.ru/3.11/glossary.html#term-GIL) `asyncio.to_thread()` обычно можно использовать только для того, чтобы сделать функции, связанные с вводом-выводом, неблокирующими. Однако для модулей расширения, которые освобождают GIL, или альтернативных реализаций Python, у которых его нет, `asyncio.to_thread()` также можно использовать для функций, связанных с процессором.686687Новое в версии 3.9.688689## [Планирование из других потоков](https://python-all.ru/3.11/library/asyncio-task.html#id12)690691#### `asyncio.run_coroutine_threadsafe(coro, loop)`692693Отправляет корутину в указанный цикл событий. Потокобезопасно.694695Возвращает [`concurrent.futures.Future`](https://python-all.ru/3.11/library/concurrent.futures.html#concurrent.futures.Future) для ожидания результата из другого потока операционной системы.696697Эта функция предназначена для вызова из другого потока операционной системы, отличного от того, в котором работает цикл событий. Пример:698699```python700# Создать корутину701coro = asyncio.sleep(1, result=3)702703# Отправить корутину в заданный цикл704future = asyncio.run_coroutine_threadsafe(coro, loop)705706# Ожидать результат с необязательным аргументом таймаута707assert future.result(timeout) == 3708```709710Если в корутине возникло исключение, возвращаемый объект Future получит уведомление. Его также можно использовать для отмены задачи в цикле событий:711712```python713try:714    result = future.result(timeout)715except TimeoutError:716    print('The coroutine took too long, cancelling the task...')717    future.cancel()718except Exception as exc:719    print(f'The coroutine raised an exception: {exc!r}')720else:721    print(f'The coroutine returned: {result!r}')722```723724См. раздел [concurrency and multithreading](https://python-all.ru/3.11/library/asyncio-dev.html#asyncio-multithreading) документации.725726В отличие от других функций asyncio, эта функция требует явной передачи аргумента *loop* .727728Новое в версии 3.5.1.729730## [Интроспекция](https://python-all.ru/3.11/library/asyncio-task.html#id13)731732#### `asyncio.current_task(loop=None)`733734Возвращает экземпляр текущей выполняющейся [`Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task) или `None`, если нет выполняющейся задачи.735736Если *loop* равен `None`, используется [`get_running_loop()`](https://python-all.ru/3.11/library/asyncio-eventloop.html#asyncio.get_running_loop) для получения текущего цикла.737738Добавлено в версии 3.7.739740#### `asyncio.all_tasks(loop=None)`741742Возвращает набор ещё не завершённых объектов [`Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task), выполняемых циклом.743744Если *loop* равен `None`, [`get_running_loop()`](https://python-all.ru/3.11/library/asyncio-eventloop.html#asyncio.get_running_loop) используется для получения текущего цикла.745746Добавлено в версии 3.7.747748#### `asyncio.iscoroutine(obj)`749750Возвращает `True`, если *obj* является объектом корутины.751752Новое в версии 3.4.753754## [Объект Задачи](https://python-all.ru/3.11/library/asyncio-task.html#id14)755756#### `class asyncio.Task(coro, *, loop=None, name=None, context=None)`757758Объект [`Future-like`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future), выполняющий корутину Python [корутину](https://python-all.ru/3.11/library/asyncio-task.html#coroutine). Непотокобезопасный.759760Задачи используются для выполнения корутин в циклах событий. Если корутина ожидает Future, задача приостанавливает выполнение корутины и ждёт завершения Future. Когда Future *завершён*, выполнение обёрнутой корутины возобновляется.761762Циклы событий используют кооперативное планирование: цикл событий выполняет по одной задаче за раз. Пока задача ожидает завершения Future, цикл событий выполняет другие задачи, колбэки или производит операции ввода-вывода.763764Используйте высокоуровневую функцию [`asyncio.create_task()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.create_task) для создания задач или низкоуровневые функции [`loop.create_task()`](https://python-all.ru/3.11/library/asyncio-eventloop.html#asyncio.loop.create_task) или [`ensure_future()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.ensure_future). Ручное создание экземпляров задач не рекомендуется.765766Чтобы отменить выполняющуюся задачу, используйте метод [`cancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel). Его вызов приведёт к тому, что задача возбудит исключение [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) в обёрнутой корутине. Если во время отмены корутина ожидает объект Future, этот объект Future будет отменён.767768[`cancelled()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancelled) можно использовать для проверки, была ли задача отменена. Метод возвращает `True`, если обёрнутая корутина не подавила исключение [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) и была действительно отменена.769770[`asyncio.Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task) наследует от [`Future`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future) все свои API, за исключением [`Future.set_result()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future.set_result) и [`Future.set_exception()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future.set_exception).771772Необязательный именованный аргумент *context* позволяет задать пользовательский [`contextvars.Context`](https://python-all.ru/3.11/library/contextvars.html#contextvars.Context) для выполнения *coro*. Если *context* не указан, задача копирует текущий контекст и затем выполняет свою корутину в скопированном контексте.773774Изменено в версии 3.7: Добавлена поддержка модуля [`contextvars`](https://python-all.ru/3.11/library/contextvars.html#module-contextvars).775776Изменено в версии 3.8: Добавлен параметр *name*.777778Устарело с версии 3.10: Предупреждение об устаревании выводится, если *loop* не указан и нет работающего цикла событий.779780Изменено в версии 3.11: Добавлен параметр *context*.781782#### `done()`783784Возвращает `True`, если задача *завершена*.785786Задача *завершена*, когда обёрнутая корутина вернула значение, возбудила исключение или задача была отменена.787788#### `result()`789790Возвращает результат задачи.791792Если задача *завершена*, возвращается результат обёрнутой корутины (или, если корутина возбудила исключение, это исключение возбуждается повторно).793794Если задача была *отменена*, этот метод возбуждает исключение [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError).795796Если результат задачи ещё не доступен, этот метод возбуждает исключение [`InvalidStateError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.InvalidStateError).797798#### `exception()`799800Возвращает исключение задачи.801802Если обёрнутая корутина возбудила исключение, возвращается это исключение. Если корутина завершилась нормально, этот метод возвращает `None`.803804Если задача была *отменена*, этот метод возбуждает исключение [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError).805806Если задача ещё не *завершена*, этот метод возбуждает исключение [`InvalidStateError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.InvalidStateError).807808#### `add_done_callback(callback, *, context=None)`809810Добавляет колбэк, который будет выполнен, когда задача *завершена*.811812Этот метод следует использовать только в низкоуровневом коде, основанном на колбэках.813814Подробнее см. в документации [`Future.add_done_callback()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future.add_done_callback).815816#### `remove_done_callback(callback)`817818Удаляет *колбэк* из списка колбэков.819820Этот метод следует использовать только в низкоуровневом коде, основанном на колбэках.821822Подробнее см. в документации [`Future.remove_done_callback()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future.remove_done_callback).823824#### `get_stack(*, limit=None)`825826Возвращает список кадров стека для этой задачи.827828Если обёрнутая корутина не завершена, возвращается стек, в котором она приостановлена. Если корутина успешно завершилась или была отменена, возвращается пустой список. Если корутина была завершена исключением, возвращается список кадров трассировки.829830Кадры всегда упорядочены от старых к новым.831832Для приостановленной корутины возвращается только один кадр стека.833834Необязательный аргумент *limit* задаёт максимальное количество возвращаемых кадров; по умолчанию возвращаются все доступные кадры. Порядок возвращаемого списка различается в зависимости от того, возвращается стек или трассировка: возвращаются новейшие кадры стека, но старейшие кадры трассировки. (Это соответствует поведению модуля traceback.)835836#### `print_stack(*, limit=None, file=None)`837838Печатает стек или трассировку для этой задачи.839840Результат аналогичен выводу модуля traceback для кадров, полученных с помощью [`get_stack()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.get_stack).841842Аргумент *limit* передаётся непосредственно в [`get_stack()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.get_stack).843844Аргумент *file* – это поток ввода-вывода, в который записывается вывод; по умолчанию вывод записывается в [`sys.stdout`](https://python-all.ru/3.11/library/sys.html#sys.stdout).845846#### `get_coro()`847848Возвращает объект корутины, обёрнутый [`Task`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task).849850Новое в версии 3.8.851852#### `get_name()`853854Возвращает имя задачи.855856Если задаче не было явно присвоено имя, стандартная реализация задач asyncio генерирует имя по умолчанию при создании.857858Новое в версии 3.8.859860#### `set_name(value)`861862Устанавливает имя задачи.863864Аргумент *value* может быть любым объектом, который затем преобразуется в строку.865866В стандартной реализации задач имя будет видно в [`repr()`](https://python-all.ru/3.11/library/functions.html#repr)-выводе объекта задачи.867868Новое в версии 3.8.869870#### `cancel(msg=None)`871872Запрашивает отмену задачи.873874Обеспечивает выброс исключения [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) в обёрнутую корутину на следующем цикле событийного цикла.875876Затем корутина получает возможность выполнить очистку или даже отклонить запрос, подавив исключение с помощью блока [`try`](https://python-all.ru/3.11/reference/compound_stmts.html#try) … … `except CancelledError` … [`finally`](https://python-all.ru/3.11/reference/compound_stmts.html#finally). Поэтому, в отличие от [`Future.cancel()`](https://python-all.ru/3.11/library/asyncio-future.html#asyncio.Future.cancel), [`Task.cancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel) не гарантирует, что задача будет отменена, хотя полное подавление отмены не является обычной практикой и активно не рекомендуется. Если корутина всё же решает подавить отмену, ей необходимо вызвать [`Task.uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel) в дополнение к перехвату исключения.877878Изменено в версии 3.9: Добавлен параметр *msg*.879880Изменено в версии 3.11: Параметр `msg` передаётся от отменённой задачи к ожидающему её объекту.881882Следующий пример показывает, как корутины могут перехватить запрос отмены:883884```python885async def cancel_me():886    print('cancel_me(): before sleep')887888    try:889        # Ждать 1 час890        await asyncio.sleep(3600)891    except asyncio.CancelledError:892        print('cancel_me(): cancel sleep')893        raise894    finally:895        print('cancel_me(): after sleep')896897async def main():898    # Создать задачу "cancel_me"899    task = asyncio.create_task(cancel_me())900901    # Ждать 1 секунду902    await asyncio.sleep(1)903904    task.cancel()905    try:906        await task907    except asyncio.CancelledError:908        print("main(): cancel_me is cancelled now")909910asyncio.run(main())911912# Ожидаемый вывод:913#914#     cancel_me(): перед сном915#     cancel_me(): отмена сна916#     cancel_me(): после сна917#     main(): cancel_me теперь отменён918```919920#### `cancelled()`921922Return `True` if the Task is *cancelled*.923924The Task is *cancelled* when the cancellation was requested with [`cancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel) and the wrapped coroutine propagated the [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError) exception thrown into it.925926#### `uncancel()`927928Уменьшает счётчик запросов отмены для этой задачи.929930Возвращает оставшееся количество запросов отмены.931932Обратите внимание: после завершения выполнения отменённой задачи последующие вызовы [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel) неэффективны.933934Новое в версии 3.11.935936Этот метод используется внутри asyncio и не предназначен для использования в пользовательском коде. В частности, если отмена задачи была успешно отменена, это позволяет элементам структурированной конкурентности, таким как [Группы задач](https://python-all.ru/3.11/library/asyncio-task.html#taskgroups) и [`asyncio.timeout()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.timeout), продолжать выполнение, изолируя отмену в соответствующем структурированном блоке. Например:937938```python939async def make_request_with_timeout():940    try:941        async with asyncio.timeout(1):942            # Структурированный блок, на который влияет таймаут:943            await make_request()944            await make_another_request()945    except TimeoutError:946        log("There was a timeout")947    # Внешний код, на который таймаут не влияет:948    await unrelated_code()949```950951Хотя блок с `make_request()` и `make_another_request()` может быть отменён из-за тайм-аута, `unrelated_code()` должен продолжить работу даже в случае тайм-аута. Это реализовано с помощью [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel). Менеджеры контекста [`TaskGroup`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.TaskGroup) используют [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel) аналогичным образом.952953Если код конечного пользователя по какой-то причине подавляет отмену, перехватывая [`CancelledError`](https://python-all.ru/3.11/library/asyncio-exceptions.html#asyncio.CancelledError), ему необходимо вызвать этот метод, чтобы снять состояние отмены.954955#### `cancelling()`956957Возвращает количество ожидающих запросов отмены для этой задачи, т.е. количество вызовов [`cancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancel) минус количество вызовов [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel).958959Обратите внимание: если это число больше нуля, но задача всё ещё выполняется, [`cancelled()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.cancelled) всё равно вернёт `False`. Это связано с тем, что это число можно уменьшить вызовом [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel), что может привести к тому, что задача в итоге не будет отменена, если количество запросов отмены снизится до нуля.960961Этот метод используется во внутренних механизмах asyncio и не предназначен для использования в пользовательском коде. Подробнее см. [`uncancel()`](https://python-all.ru/3.11/library/asyncio-task.html#asyncio.Task.uncancel).962963Новое в версии 3.11.964