By Marcelo Fernandes Sep 11, 2017
Coroutines used with asyncio may be implemented using the async def
statement, or by using generators.
The async def
type of coroutine was added in Python 3.5, and is recommended.
Generator-based coroutines should be decorated with @asyncio.coroutine
, although this is not strictly enforced.
The decorator enables compatibility with async def
coroutines, and also serves as documentation.
Generator-based coroutines use the yield from syntax, instead of the yield syntax.
Few things a coroutine can do:
result = await future
or result = yield from future
- suspends the coroutine until the future is done,
then returns the future's result, or raises an exception, which will be propagated. (if the future is canceled, it will raise
a CancelledError exception). Note that tasks are futures and everything said about futures also applies to tasks.result = await coroutine
or result = yield from coroutine
- wait for another coroutine to produce
a results (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine.return expression
- produces a result to the coroutine that is waiting for this one using await or yield fromraise exception
- raises an exception in the coroutine that is waiting for this one using await or yield from@asyncio.coroutine
Decorator to mark generator-based coroutines. This enables the generator use yield from
to call
async def
coroutines, and also enables the generator to be called by async def
coroutines,
for instance using an await expression.
There is no need to decorate async def
coroutines themselves.
import asyncio
async def hello_world():
print("Hello World!")
loop = asyncio.get_event_loop()
# Blocking call which returns when the hello_world() coroutine is done
loop.run_until_complete(hello_world())
loop.close()
import asyncio
import datetime
async def display_date(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)
loop = asyncio.get_event_loop()
# Blocking call which returns when the display_date() coroutine is done
loop.run_until_complete(display_date(loop))
loop.close()
cancel()
Cancel the future and schedule callbacks.
If the future is already done or cancelled, return False
. Otherwise,
change the future's state to cancelled, schedule the callbacks and return True
cancelled()
Returns a boolean.
done()
Returns a boolean
result()
Return the result this future represents.
If the future has been cancelled, raises a CancelledError
, if the future's result ins't yet available,
raises InvalidStateError
. If the furue is done and has an exception set, this exception is raised.
exception()
Return the exception that was set on this future.
add_done_callback(fn)
Add a callback to be run when the future becomes done.
The callback is called with a single argument - the future object. If the future is already done when this is called,
the callback is scheduled with call_soon()
use functools.partial to pass parameters to the callback. Example: fut.add_done_callback(functools.partial(print, "Future:", flush=True))
remove_done_callback(fn)
Remove all instances of a callback from the "call when done" list.
set_result(result)
Mark the future done and set its result
set_exception(exception)
Mark the future done and set an exception.
import asyncio
async def slow_operation(future):
await asyncio.sleep(1)
future.set_result('Future is done!')
loop = asyncio.get_event_loop()
future = asyncio.Future()
asyncio.ensure_future(slow_operation(future))
loop.run_until_complete(future)
print(future.result())
loop.close()
Schedule the execution of a coroutine: wrap it in a future. A task is a subclass of Future
A task is responsible for executing a coroutine object in an event loop. If the wrapped coroutine yields from a future,
the task suspends the execution of the wrapped coroutine and waits for the completion of the future. When the future is done, the execution
of the wrapped coroutine restarts with the result or the exception of the future.
Event loops use cooperative scheduling: an event loop only runs one task at a time. Other tasks may run in parallel
if other event loops are running in different threads. While a task waits for the completion of a future, the event loop
executes a new task.
Don't directely create Task instances: use the ensure_future() function.
This class is not thread safe.
all_tasks(loop=None)
Return a set of all tasks for an event loop. By Default, all tasks for the current event loop are returned
current_task(loop=None)
Return the currently running task in an event loop or None
cancel()
Request that this task cancel itself.
get_stack(*, limit=None)
Return the list of stack frames for this task's coroutine.
print_stack(*, limit=None, file=None)
Print the stack or traceback for this task's coroutine.
import asyncio
async def factorial(name, number):
f = 1
for i in range(2, number+1):
print("Task %s: Compute factorial(%s)..." % (name, i))
await asyncio.sleep(1)
f *= i
print("Task %s: factorial(%s) = %s" % (name, number, f))
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
factorial("A", 2),
factorial("B", 3),
factorial("C", 4),
))
loop.close()
Output:
Task A: Compute factorial(2)...
Task B: Compute factorial(2)...
Task C: Compute factorial(2)...
Task A: factorial(2) = 2
Task B: Compute factorial(3)...
Task C: Compute factorial(3)...
Task B: factorial(3) = 6
Task C: Compute factorial(4)...
Task C: factorial(4) = 24