9.6. AsyncIO Run¶
asyncio.run()
is a main entrypointasyncio.gather()
can run concurrently and gather result (in order of its arguments)
9.6.1. SetUp¶
>>> import asyncio
>>>
>>>
9.6.2. Run Coroutine¶
asyncio.run(coro, *, debug=False)
Execute the coroutine
coro
and return the resultTakes care of managing the asyncio event loop, finalizing asynchronous generators, and closing the threadpool
Cannot be called when another asyncio event loop is running in the same thread
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
>>> async def hello():
... print('hello')
>>>
>>>
>>> asyncio.run(hello())
hello
9.6.3. Run Sequentially¶
>>> async def hello():
... print('hello')
>>>
>>>
>>> async def main():
... await hello()
... await hello()
... await hello()
>>>
>>>
>>> asyncio.run(main())
hello
hello
hello
9.6.4. Run Concurrently¶
awaitable
asyncio.gather(*aws, return_exceptions=False)
Run awaitable objects in the
aws
sequence concurrentlyIf any awaitable in
aws
is a coroutine, it is automatically scheduled as aTask
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
return_exceptions=False
(default): the first raised exception is immediately propagated to the task that awaits ongather()
; other awaitables in theaws
sequence won't be cancelled and will continue to runreturn_exceptions=True
: exceptions are treated the same as successful results, and aggregated in the result listIf
gather()
is cancelled (ie. on timeout), all submitted awaitables (that have not completed yet) are also cancelledIf any
Task
orFuture
from theaws
sequence is cancelled, it is treated as if it raisedCancelledError
– thegather()
call is not cancelled in this caseThis is to prevent the cancellation of one submitted Task/Future to cause other Tasks/Futures to be cancelled
>>> async def a():
... print('a: started')
... await asyncio.sleep(0.2)
... print('a: finished')
... return 'a'
>>>
>>> async def b():
... print('b: started')
... await asyncio.sleep(0.1)
... print('b: finished')
... return 'b'
>>>
>>> async def c():
... print('c: started')
... await asyncio.sleep(0.3)
... print('c: finished')
... return 'c'
>>>
>>>
>>> async def main():
... result = await asyncio.gather(a(), b(), c())
... print(f'Result: {result}')
>>>
>>>
>>> asyncio.run(main())
a: started
b: started
c: started
b: finished
a: finished
c: finished
Result: ['a', 'b', 'c']
9.6.5. Run as Completed¶
asyncio.as_completed(aws, *, timeout=None)
Run awaitable objects in the
aws
iterable concurrentlyReturn an iterator of coroutines
Each coroutine returned can be awaited to get the earliest next result from the iterable of the remaining awaitables
Raises
asyncio.TimeoutError
if the timeout occurs before all Futures are done
>>> async def a():
... print('a: started')
... await asyncio.sleep(0.2)
... print('a: finished')
... return 'a'
>>>
>>> async def b():
... print('b: started')
... await asyncio.sleep(0.1)
... print('b: finished')
... return 'b'
>>>
>>> async def c():
... print('c: started')
... await asyncio.sleep(0.3)
... print('c: finished')
... return 'c'
>>>
>>>
>>> async def main():
... todo = [a(), b(), c()]
... for coro in asyncio.as_completed(todo):
... result = await coro
... print(result)
>>>
>>>
>>> asyncio.run(main())
a: started
c: started
b: started
b: finished
b
a: finished
a
c: finished
c
9.6.6. Run in Threads¶
coroutine
asyncio.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.Return a coroutine that can be awaited to get the eventual result of func.
This coroutine function is 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.
>>> import asyncio
>>> import time
>>>
>>>
>>> def work():
... print(f'Work started {time.strftime("%X")}')
... time.sleep(2) # Blocking
... print(f'Work done at {time.strftime("%X")}')
>>>
>>>
>>> async def main():
... print(f'Started main at {time.strftime("%X")}')
... await asyncio.gather(
... asyncio.to_thread(work),
... asyncio.sleep(1))
... print(f'Finished main at {time.strftime("%X")}')
>>>
>>>
>>> asyncio.run(main())
Started main at 22:53:40
Work started 22:53:40
Work done at 22:53:42
Finished main at 22:53:42
Due to the 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.