I’ve seen several basic Python 3.5 tutorials on asyncio doing the same operation in various flavours.
In this code:
import asyncio async def doit(i): print("Start %d" % i) await asyncio.sleep(3) print("End %d" % i) return i if __name__ == '__main__': loop = asyncio.get_event_loop() #futures = [asyncio.ensure_future(doit(i), loop=loop) for i in range(10)] #futures = [loop.create_task(doit(i)) for i in range(10)] futures = [doit(i) for i in range(10)] result = loop.run_until_complete(asyncio.gather(*futures)) print(result)
All the three variants above that define the
futures variable achieve the same result; the only difference I can see is that with the third variant the execution is out of order (which should not matter in most cases). Is there any other difference? Are there cases where I can’t just use the simplest variant (plain list of coroutines)?
Starting from Python 3.7
asyncio.create_task(coro) high-level function was added for this purpose.
You should use it instead other ways of creating tasks from coroutimes. However if you need to create task from arbitrary awaitable, you should use
create_task is an abstract method of
AbstractEventLoop. Different event loops can implement this function different ways.
You should use
ensure_future to create tasks. You’ll need
create_task only if you’re going to implement your own event loop type.
@bj0 pointed at Guido’s answer on this topic:
The point of
ensure_future()is if you have something that could
either be a coroutine or a
Future(the latter includes a
that’s a subclass of
Future), and you want to be able to call a method
on it that is only defined on
Future(probably about the only useful
cancel()). When it is already a
does nothing; when it is a coroutine it wraps it in a
If you know that you have a coroutine and you want it to be scheduled,
the correct API to use is
create_task(). The only time when you should
ensure_future()is when you are providing an API (like most
of asyncio’s own APIs) that accepts either a coroutine or a
you need to do something to it that requires you to have a
In the end I still believe that
ensure_future()is an appropriately
obscure name for a rarely-needed piece of functionality. When creating
a task from a coroutine you should use the appropriately-named
loop.create_task(). Maybe there should be an alias for that
It’s surprising to me. My main motivation to use
ensure_future all along was that it’s higher-level function comparing to loop’s member
create_task (discussion contains some ideas like adding
I can also point that in my opinion it’s pretty convenient to use universal function that can handle any
Awaitable rather than coroutines only.
However, Guido’s answer is clear: “When creating a task from a coroutine you should use the appropriately-named
When coroutines should be wrapped in tasks?
Wrap coroutine in a Task – is a way to start this coroutine “in background”. Here’s example:
import asyncio async def msg(text): await asyncio.sleep(0.1) print(text) async def long_operation(): print('long_operation started') await asyncio.sleep(3) print('long_operation finished') async def main(): await msg('first') # Now you want to start long_operation, but you don't want to wait it finised: # long_operation should be started, but second msg should be printed immediately. # Create task to do so: task = asyncio.ensure_future(long_operation()) await msg('second') # Now, when you want, you can await task finised: await task if __name__ == "__main__": loop = asyncio.get_event_loop() loop.run_until_complete(main())
first long_operation started second long_operation finished
You can replace
asyncio.ensure_future(long_operation()) with just
await long_operation() to feel the difference.
- accepts coroutines,
- returns Task,
- it is invoked in context of the loop.
- accepts Futures, coroutines, awaitable objects,
- returns Task (or Future if Future passed).
- if the given arg is a coroutine it uses
- loop object can be passed.
As you can see the create_task is more specific.
async function without create_task or ensure_future
async function returns coroutine
async def doit(i): await asyncio.sleep(3) return i doit(4) <coroutine object doit at 0x7f91e8e80ba0>
And since the
gather under the hood ensures (
ensure_future) that args are futures, explicitly
ensure_future is redundant.
Note: Only valid for Python 3.7 (for Python 3.5 refer to the earlier answer).
From the official docs:
So now, in Python 3.7 onwards, there are 2 top-level wrapper function (similar but different):
asyncio.create_task: which simply call
event_loop.create_task(coro)directly. (see source code)
ensure_futurewhich also call
event_loop.create_task(coro)if it is coroutine or else it is simply to ensure the return type to be a asyncio.Future. (see source code). Anyway,
Taskis still a
Futuredue to its class inheritance (ref).
Well, utlimately both of these wrapper functions will help you call
BaseEventLoop.create_task. The only difference is
ensure_future accept any
awaitable object and help you convert it into a Future. And also you can provide your own
event_loop parameter in
ensure_future. And depending if you need those capability or not, you can simply choose which wrapper to use.
for your example, all the three types execute asynchronously. the only difference is that, in the third example, you pre-generated all 10 coroutines, and submitted to the loop together. so only the last one gives output randomly.