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)?
Actual info:
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
asyncio.ensure_future(obj)
.Old info:
ensure_future
vscreate_task
ensure_future
is a method to createTask
fromcoroutine
. It creates tasks in different ways based on argument (including using ofcreate_task
for coroutines and future-like objects).create_task
is an abstract method ofAbstractEventLoop
. Different event loops can implement this function different ways.You should use
ensure_future
to create tasks. You'll needcreate_task
only if you're going to implement your own event loop type.Upd:
@bj0 pointed at Guido's answer on this topic:
and later:
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 membercreate_task
(discussion contains some ideas like addingasyncio.spawn
orasyncio.create_task
).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
loop.create_task()
"When coroutines should be wrapped in tasks?
Wrap coroutine in a Task - is a way to start this coroutine "in background". Here's example:
Output:
You can replace
asyncio.ensure_future(long_operation())
with justawait long_operation()
to feel the difference.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.
create_task()
ensure_future()
create_task
,As you can see the create_task is more specific.
async
function without create_task or ensure_futureSimple invoking
async
function returns coroutineAnd since the
gather
under the hood ensures (ensure_future
) that args are futures, explicitlyensure_future
is redundant.Similar question What's the difference between loop.create_task, asyncio.async/ensure_future and Task?
From the official docs:
Detail:
So now, in Python 3.7 onwards, there are 2 top-level wrapper function (similar but different):
asyncio.create_task
: which simply callevent_loop.create_task(coro)
directly. (see source code)ensure_future
which also callevent_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,Task
is still aFuture
due to its class inheritance (ref).Well, utlimately both of these wrapper functions will help you call
BaseEventLoop.create_task
. The only difference isensure_future
accept anyawaitable
object and help you convert it into a Future. And also you can provide your ownevent_loop
parameter inensure_future
. And depending if you need those capability or not, you can simply choose which wrapper to use.