Skip to content

Latest commit

 

History

History
1025 lines (690 loc) · 30.1 KB

asyncio-task.rst

File metadata and controls

1025 lines (690 loc) · 30.1 KB
.. currentmodule:: asyncio


Coroutines and Tasks

This section outlines high-level asyncio APIs to work with coroutines and Tasks.

:term:`Coroutines <coroutine>` declared with the async/await syntax is the preferred way of writing asyncio applications. For example, the following snippet of code prints "hello", waits 1 second, and then prints "world":

>>> import asyncio

>>> async def main():
...     print('hello')
...     await asyncio.sleep(1)
...     print('world')

>>> asyncio.run(main())
hello
world

Note that simply calling a coroutine will not schedule it to be executed:

>>> main()
<coroutine object main at 0x1053bb7c8>

To actually run a coroutine, asyncio provides three main mechanisms:

  • The :func:`asyncio.run` function to run the top-level entry point "main()" function (see the above example.)

  • Awaiting on a coroutine. The following snippet of code will print "hello" after waiting for 1 second, and then print "world" after waiting for another 2 seconds:

    import asyncio
    import time
    
    async def say_after(delay, what):
        await asyncio.sleep(delay)
        print(what)
    
    async def main():
        print(f"started at {time.strftime('%X')}")
    
        await say_after(1, 'hello')
        await say_after(2, 'world')
    
        print(f"finished at {time.strftime('%X')}")
    
    asyncio.run(main())
    

    Expected output:

    started at 17:13:52
    hello
    world
    finished at 17:13:55
    
  • The :func:`asyncio.create_task` function to run coroutines concurrently as asyncio :class:`Tasks <Task>`.

    Let's modify the above example and run two say_after coroutines concurrently:

    async def main():
        task1 = asyncio.create_task(
            say_after(1, 'hello'))
    
        task2 = asyncio.create_task(
            say_after(2, 'world'))
    
        print(f"started at {time.strftime('%X')}")
    
        # Wait until both tasks are completed (should take
        # around 2 seconds.)
        await task1
        await task2
    
        print(f"finished at {time.strftime('%X')}")
    

    Note that expected output now shows that the snippet runs 1 second faster than before:

    started at 17:14:32
    hello
    world
    finished at 17:14:34
    

We say that an object is an awaitable object if it can be used in an :keyword:`await` expression. Many asyncio APIs are designed to accept awaitables.

There are three main types of awaitable objects: coroutines, Tasks, and Futures.

Coroutines

Python coroutines are awaitables and therefore can be awaited from other coroutines:

import asyncio

async def nested():
    return 42

async def main():
    # Nothing happens if we just call "nested()".
    # A coroutine object is created but not awaited,
    # so it *won't run at all*.
    nested()

    # Let's do it differently now and await it:
    print(await nested())  # will print "42".

asyncio.run(main())

Important

In this documentation the term "coroutine" can be used for two closely related concepts:

  • a coroutine function: an :keyword:`async def` function;
  • a coroutine object: an object returned by calling a coroutine function.

asyncio also supports legacy :ref:`generator-based <asyncio_generator_based_coro>` coroutines.

Tasks

Tasks are used to schedule coroutines concurrently.

When a coroutine is wrapped into a Task with functions like :func:`asyncio.create_task` the coroutine is automatically scheduled to run soon:

import asyncio

async def nested():
    return 42

async def main():
    # Schedule nested() to run soon concurrently
    # with "main()".
    task = asyncio.create_task(nested())

    # "task" can now be used to cancel "nested()", or
    # can simply be awaited to wait until it is complete:
    await task

asyncio.run(main())

Futures

A :class:`Future` is a special low-level awaitable object that represents an eventual result of an asynchronous operation.

When a Future object is awaited it means that the coroutine will wait until the Future is resolved in some other place.

Future objects in asyncio are needed to allow callback-based code to be used with async/await.

Normally there is no need to create Future objects at the application level code.

Future objects, sometimes exposed by libraries and some asyncio APIs, can be awaited:

async def main():
    await function_that_returns_a_future_object()

    # this is also valid:
    await asyncio.gather(
        function_that_returns_a_future_object(),
        some_python_coroutine()
    )

A good example of a low-level function that returns a Future object is :meth:`loop.run_in_executor`.

.. function:: run(coro, *, debug=False)

    Execute the :term:`coroutine` *coro* and return the result.

    This function runs the passed coroutine, taking care of
    managing the asyncio event loop, *finalizing asynchronous
    generators*, and closing the threadpool.

    This function cannot be called when another asyncio event loop is
    running in the same thread.

    If *debug* is ``True``, the event loop will be run in debug mode.

    This function always creates a new event loop and closes it at
    the end.  It should be used as a main entry point for asyncio
    programs, and should ideally only be called once.

    Example::

        async def main():
            await asyncio.sleep(1)
            print('hello')

        asyncio.run(main())

    .. versionadded:: 3.7

    .. versionchanged:: 3.9
       Updated to use :meth:`loop.shutdown_default_executor`.

    .. note::
       The source code for ``asyncio.run()`` can be found in
       :source:`Lib/asyncio/runners.py`.

.. function:: create_task(coro, *, name=None)

   Wrap the *coro* :ref:`coroutine <coroutine>` into a :class:`Task`
   and schedule its execution.  Return the Task object.

   If *name* is not ``None``, it is set as the name of the task using
   :meth:`Task.set_name`.

   The task is executed in the loop returned by :func:`get_running_loop`,
   :exc:`RuntimeError` is raised if there is no running loop in
   current thread.

   .. important::

      Save a reference to the result of this function, to avoid
      a task disappearing mid execution.

   .. versionadded:: 3.7

   .. versionchanged:: 3.8
      Added the ``name`` parameter.


.. coroutinefunction:: sleep(delay, result=None, *, loop=None)

   Block for *delay* seconds.

   If *result* is provided, it is returned to the caller
   when the coroutine completes.

   ``sleep()`` always suspends the current task, allowing other tasks
   to run.

   Setting the delay to 0 provides an optimized path to allow other
   tasks to run. This can be used by long-running functions to avoid
   blocking the event loop for the full duration of the function call.

   .. deprecated-removed:: 3.8 3.10
      The *loop* parameter.

   .. _asyncio_example_sleep:

   Example of coroutine displaying the current date every second
   for 5 seconds::

    import asyncio
    import datetime

    async def display_date():
        loop = asyncio.get_running_loop()
        end_time = loop.time() + 5.0
        while True:
            print(datetime.datetime.now())
            if (loop.time() + 1.0) >= end_time:
                break
            await asyncio.sleep(1)

    asyncio.run(display_date())


.. awaitablefunction:: gather(*aws, loop=None, return_exceptions=False)

   Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
   sequence *concurrently*.

   If any awaitable in *aws* is a coroutine, it is automatically
   scheduled as a Task.

   If all awaitables are completed successfully, the result is an
   aggregate list of returned values.  The order of result values
   corresponds to the order of awaitables in *aws*.

   If *return_exceptions* is ``False`` (default), the first
   raised exception is immediately propagated to the task that
   awaits on ``gather()``.  Other awaitables in the *aws* sequence
   **won't be cancelled** and will continue to run.

   If *return_exceptions* is ``True``, exceptions are treated the
   same as successful results, and aggregated in the result list.

   If ``gather()`` is *cancelled*, all submitted awaitables
   (that have not completed yet) are also *cancelled*.

   If any Task or Future from the *aws* sequence is *cancelled*, it is
   treated as if it raised :exc:`CancelledError` -- the ``gather()``
   call is **not** cancelled in this case.  This is to prevent the
   cancellation of one submitted Task/Future to cause other
   Tasks/Futures to be cancelled.

   .. deprecated-removed:: 3.8 3.10
      The *loop* parameter.

   .. _asyncio_example_gather:

   Example::

      import asyncio

      async def factorial(name, number):
          f = 1
          for i in range(2, number + 1):
              print(f"Task {name}: Compute factorial({number}), currently i={i}...")
              await asyncio.sleep(1)
              f *= i
          print(f"Task {name}: factorial({number}) = {f}")
          return f

      async def main():
          # Schedule three calls *concurrently*:
          L = await asyncio.gather(
              factorial("A", 2),
              factorial("B", 3),
              factorial("C", 4),
          )
          print(L)

      asyncio.run(main())

      # Expected output:
      #
      #     Task A: Compute factorial(2), currently i=2...
      #     Task B: Compute factorial(3), currently i=2...
      #     Task C: Compute factorial(4), currently i=2...
      #     Task A: factorial(2) = 2
      #     Task B: Compute factorial(3), currently i=3...
      #     Task C: Compute factorial(4), currently i=3...
      #     Task B: factorial(3) = 6
      #     Task C: Compute factorial(4), currently i=4...
      #     Task C: factorial(4) = 24
      #     [2, 6, 24]

   .. note::
      If *return_exceptions* is False, cancelling gather() after it
      has been marked done won't cancel any submitted awaitables.
      For instance, gather can be marked done after propagating an
      exception to the caller, therefore, calling ``gather.cancel()``
      after catching an exception (raised by one of the awaitables) from
      gather won't cancel any other awaitables.

   .. versionchanged:: 3.7
      If the *gather* itself is cancelled, the cancellation is
      propagated regardless of *return_exceptions*.


.. awaitablefunction:: shield(aw, *, loop=None)

   Protect an :ref:`awaitable object <asyncio-awaitables>`
   from being :meth:`cancelled <Task.cancel>`.

   If *aw* is a coroutine it is automatically scheduled as a Task.

   The statement::

       res = await shield(something())

   is equivalent to::

       res = await something()

   *except* that if the coroutine containing it is cancelled, the
   Task running in ``something()`` is not cancelled.  From the point
   of view of ``something()``, the cancellation did not happen.
   Although its caller is still cancelled, so the "await" expression
   still raises a :exc:`CancelledError`.

   If ``something()`` is cancelled by other means (i.e. from within
   itself) that would also cancel ``shield()``.

   If it is desired to completely ignore cancellation (not recommended)
   the ``shield()`` function should be combined with a try/except
   clause, as follows::

       try:
           res = await shield(something())
       except CancelledError:
           res = None

   .. deprecated-removed:: 3.8 3.10
      The *loop* parameter.


.. coroutinefunction:: wait_for(aw, timeout, *, loop=None)

   Wait for the *aw* :ref:`awaitable <asyncio-awaitables>`
   to complete with a timeout.

   If *aw* is a coroutine it is automatically scheduled as a Task.

   *timeout* can either be ``None`` or a float or int number of seconds
   to wait for.  If *timeout* is ``None``, block until the future
   completes.

   If a timeout occurs, it cancels the task and raises
   :exc:`asyncio.TimeoutError`.

   To avoid the task :meth:`cancellation <Task.cancel>`,
   wrap it in :func:`shield`.

   The function will wait until the future is actually cancelled,
   so the total wait time may exceed the *timeout*. If an exception
   happens during cancellation, it is propagated.

   If the wait is cancelled, the future *aw* is also cancelled.

   .. deprecated-removed:: 3.8 3.10
      The *loop* parameter.

   .. _asyncio_example_waitfor:

   Example::

       async def eternity():
           # Sleep for one hour
           await asyncio.sleep(3600)
           print('yay!')

       async def main():
           # Wait for at most 1 second
           try:
               await asyncio.wait_for(eternity(), timeout=1.0)
           except asyncio.TimeoutError:
               print('timeout!')

       asyncio.run(main())

       # Expected output:
       #
       #     timeout!

   .. versionchanged:: 3.7
      When *aw* is cancelled due to a timeout, ``wait_for`` waits
      for *aw* to be cancelled.  Previously, it raised
      :exc:`asyncio.TimeoutError` immediately.


.. coroutinefunction:: wait(aws, *, loop=None, timeout=None,\
                            return_when=ALL_COMPLETED)

   Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
   iterable concurrently and block until the condition specified
   by *return_when*.

   The *aws* iterable must not be empty.

   Returns two sets of Tasks/Futures: ``(done, pending)``.

   Usage::

        done, pending = await asyncio.wait(aws)

   *timeout* (a float or int), if specified, can be used to control
   the maximum number of seconds to wait before returning.

   Note that this function does not raise :exc:`asyncio.TimeoutError`.
   Futures or Tasks that aren't done when the timeout occurs are simply
   returned in the second set.

   *return_when* indicates when this function should return.  It must
   be one of the following constants:

   .. tabularcolumns:: |l|L|

   +-----------------------------+----------------------------------------+
   | Constant                    | Description                            |
   +=============================+========================================+
   | :const:`FIRST_COMPLETED`    | The function will return when any      |
   |                             | future finishes or is cancelled.       |
   +-----------------------------+----------------------------------------+
   | :const:`FIRST_EXCEPTION`    | The function will return when any      |
   |                             | future finishes by raising an          |
   |                             | exception.  If no future raises an     |
   |                             | exception then it is equivalent to     |
   |                             | :const:`ALL_COMPLETED`.                |
   +-----------------------------+----------------------------------------+
   | :const:`ALL_COMPLETED`      | The function will return when all      |
   |                             | futures finish or are cancelled.       |
   +-----------------------------+----------------------------------------+

   Unlike :func:`~asyncio.wait_for`, ``wait()`` does not cancel the
   futures when a timeout occurs.

   .. deprecated:: 3.8

      If any awaitable in *aws* is a coroutine, it is automatically
      scheduled as a Task.  Passing coroutines objects to
      ``wait()`` directly is deprecated as it leads to
      :ref:`confusing behavior <asyncio_example_wait_coroutine>`.

   .. deprecated-removed:: 3.8 3.10

      The *loop* parameter.

   .. _asyncio_example_wait_coroutine:
   .. note::

      ``wait()`` schedules coroutines as Tasks automatically and later
      returns those implicitly created Task objects in ``(done, pending)``
      sets.  Therefore the following code won't work as expected::

          async def foo():
              return 42

          coro = foo()
          done, pending = await asyncio.wait({coro})

          if coro in done:
              # This branch will never be run!

      Here is how the above snippet can be fixed::

          async def foo():
              return 42

          task = asyncio.create_task(foo())
          done, pending = await asyncio.wait({task})

          if task in done:
              # Everything will work as expected now.

   .. deprecated-removed:: 3.8 3.11

      Passing coroutine objects to ``wait()`` directly is
      deprecated.


.. function:: as_completed(aws, *, loop=None, timeout=None)

   Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
   iterable concurrently.  Return an iterator of coroutines.
   Each coroutine returned can be awaited to get the earliest next
   result from the iterable of the remaining awaitables.

   Raises :exc:`asyncio.TimeoutError` if the timeout occurs before
   all Futures are done.

   .. deprecated-removed:: 3.8 3.10
      The *loop* parameter.

   Example::

       for coro in as_completed(aws):
           earliest_result = await coro
           # ...


.. coroutinefunction:: to_thread(func, /, *args, **kwargs)

   Asynchronously run function *func* in a separate thread.

   Any \*args and \*\*kwargs supplied for this function are directly passed
   to *func*. Also, the current :class:`contextvars.Context` is propagated,
   allowing context variables from the event loop thread to be accessed in the
   separate thread.

   Return a coroutine that can be awaited to get the eventual result of *func*.

   This coroutine function is primarily intended to be used for executing
   IO-bound functions/methods that would otherwise block the event loop if
   they were ran in the main thread. For example::

       def blocking_io():
           print(f"start blocking_io at {time.strftime('%X')}")
           # Note that time.sleep() can be replaced with any blocking
           # IO-bound operation, such as file operations.
           time.sleep(1)
           print(f"blocking_io complete at {time.strftime('%X')}")

       async def main():
           print(f"started main at {time.strftime('%X')}")

           await asyncio.gather(
               asyncio.to_thread(blocking_io),
               asyncio.sleep(1))

           print(f"finished main at {time.strftime('%X')}")


       asyncio.run(main())

       # Expected output:
       #
       # started main at 19:50:53
       # start blocking_io at 19:50:53
       # blocking_io complete at 19:50:54
       # finished main at 19:50:54

   Directly calling `blocking_io()` in any coroutine would block the event loop
   for its duration, resulting in an additional 1 second of run time. Instead,
   by using `asyncio.to_thread()`, we can run it in a separate thread without
   blocking the event loop.

   .. note::

      Due to the :term:`GIL`, `asyncio.to_thread()` can typically only be used
      to make IO-bound functions non-blocking. However, for extension modules
      that release the GIL or alternative Python implementations that don't
      have one, `asyncio.to_thread()` can also be used for CPU-bound functions.

   .. versionadded:: 3.9


.. function:: run_coroutine_threadsafe(coro, loop)

   Submit a coroutine to the given event loop.  Thread-safe.

   Return a :class:`concurrent.futures.Future` to wait for the result
   from another OS thread.

   This function is meant to be called from a different OS thread
   than the one where the event loop is running.  Example::

     # Create a coroutine
     coro = asyncio.sleep(1, result=3)

     # Submit the coroutine to a given loop
     future = asyncio.run_coroutine_threadsafe(coro, loop)

     # Wait for the result with an optional timeout argument
     assert future.result(timeout) == 3

   If an exception is raised in the coroutine, the returned Future
   will be notified.  It can also be used to cancel the task in
   the event loop::

     try:
         result = future.result(timeout)
     except asyncio.TimeoutError:
         print('The coroutine took too long, cancelling the task...')
         future.cancel()
     except Exception as exc:
         print(f'The coroutine raised an exception: {exc!r}')
     else:
         print(f'The coroutine returned: {result!r}')

   See the :ref:`concurrency and multithreading <asyncio-multithreading>`
   section of the documentation.

   Unlike other asyncio functions this function requires the *loop*
   argument to be passed explicitly.

   .. versionadded:: 3.5.1


.. function:: current_task(loop=None)

   Return the currently running :class:`Task` instance, or ``None`` if
   no task is running.

   If *loop* is ``None`` :func:`get_running_loop` is used to get
   the current loop.

   .. versionadded:: 3.7


.. function:: all_tasks(loop=None)

   Return a set of not yet finished :class:`Task` objects run by
   the loop.

   If *loop* is ``None``, :func:`get_running_loop` is used for getting
   current loop.

   .. versionadded:: 3.7


Note

Support for generator-based coroutines is deprecated and is removed in Python 3.11.

Generator-based coroutines predate async/await syntax. They are Python generators that use yield from expressions to await on Futures and other coroutines.

Generator-based coroutines should be decorated with :func:`@asyncio.coroutine <asyncio.coroutine>`, although this is not enforced.

.. decorator:: coroutine

    Decorator to mark generator-based coroutines.

    This decorator enables legacy generator-based coroutines to be
    compatible with async/await code::

        @asyncio.coroutine
        def old_style_coroutine():
            yield from asyncio.sleep(1)

        async def main():
            await old_style_coroutine()

    This decorator should not be used for :keyword:`async def`
    coroutines.

    .. deprecated-removed:: 3.8 3.11

       Use :keyword:`async def` instead.

.. function:: iscoroutine(obj)

   Return ``True`` if *obj* is a :ref:`coroutine object <coroutine>`.

   This method is different from :func:`inspect.iscoroutine` because
   it returns ``True`` for generator-based coroutines.

.. function:: iscoroutinefunction(func)

   Return ``True`` if *func* is a :ref:`coroutine function
   <coroutine>`.

   This method is different from :func:`inspect.iscoroutinefunction`
   because it returns ``True`` for generator-based coroutine functions
   decorated with :func:`@coroutine <coroutine>`.