Coroutine's relationship with Future
It looks like they are the same, as both can be used to get results asynchronously using the syntax of the
result = await future result = await coroutine
In fact, coroutines are generator functions that can both take arguments externally and produce results. The advantage of using coroutines is that we can pause a function and resume execution later. For example, in the case of network operations, it is possible to stop the function until a response arrives. During this time, we can switch to another task to continue execution.
A Future is more like a Promise object in Javascript. It's a placeholder whose value will be calculated in the future. In the example above, when we wait for the network IO function to complete, the function gives us a container that the Promise fills when it finishes. Once filled, we can use the callback function to get the actual result.
The Task object is a subclass of Future, which associates a coroutine with a Future, encapsulating the coroutine into a Future object.
You will generally see two methods of task initiation, the
tasks = ( asyncio.ensure_future(func1()), asyncio.ensure_future(func2()) ) loop.run_until_complete(tasks)
respond in singing
tasks = [ asyncio.ensure_future(func1()), asyncio.ensure_future(func2()) ] loop.run_until_complete((tasks))
ensure_future wraps coroutines into Tasks. Wraps some Future and coroutines into a Future.
Then it is itself a coroutine.
run_until_complete can receive both Future and coroutine objects.
BaseEventLoop.run_until_complete(future) Run until the Future is done. If the argument is a coroutine object, it is wrapped by ensure_future(). Return the Future's result, or raise its exception.
Task The correct way to exit a task
In asyncio's task loop, if you exit with CTRL-C, even if the exception is caught, the task in the Event Loop will report the following error.
Task was destroyed but it is pending!
task: <Task pending coro=<kill_me() done, defined at :5> wait_for=<Future pending cb=[Task._wakeup()]>>
According to the official documentation, a Task object is considered to exit only in the following cases, the
a result / exception are available, or that the future was cancelled
The cancel of a Task object is slightly different from its parent Future. When () is called, the corresponding coroutine throws a CancelledError exception in the next round of the event loop. The use of () does not return True immediately (which is used to indicate the end of the task); the task is not canceled until the above exception is handled.
Therefore, ending the task can be done with
for task in .all_tasks(): ()
This method will find all the tasks and cancel them.
But CTRL-C also stops the event loop, so it is necessary to restart the event loop.
try: loop.run_until_complete(tasks) except KeyboardInterrupt as e: for task in .all_tasks(): () loop.run_forever() # restart loop finally: ()
It is necessary to catch exceptions in each Task, and if you are not sure, you can use the
(..., return_exceptions=True)
Returns an exception converted to a normal result.