asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?

asyncio.ensure_future vs. BaseEventLoop.create_task vs. simple coroutine?



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)?


futures




4 Answers
4


ensure_future


create_task



ensure_future is a method to create Task from coroutine. It creates tasks in different ways based on argument (including using of create_task for coroutines and future-like objects).


ensure_future


Task


coroutine


create_task



create_task is an abstract method of AbstractEventLoop. Different event loops can implement this function different ways.


create_task


AbstractEventLoop



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.


ensure_future


create_task



Upd:



@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 Task because
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
example being cancel()). When it is already a Future (or Task) this
does nothing; when it is a coroutine it wraps it in a Task.


ensure_future()


Future


Task


Future


Future


cancel()


Future


Task


Task



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
be calling ensure_future() is when you are providing an API (like most
of asyncio's own APIs) that accepts either a coroutine or a Future and
you need to do something to it that requires you to have a Future.


create_task()


ensure_future()


Future


Future



and later:



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
asyncio.create_task()?


ensure_future()


loop.create_task()


asyncio.create_task()



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 asyncio.spawn or asyncio.create_task).


ensure_future


create_task


asyncio.spawn


asyncio.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.


Awaitable



However, Guido's answer is clear: "When creating a task from a coroutine you should use the appropriately-named loop.create_task()"


loop.create_task()



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())



Output:


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.


asyncio.ensure_future(long_operation())


await long_operation()





According to Guido, you should use create_task if you really need a a task object, which you normally shouldn't need: github.com/python/asyncio/issues/477#issuecomment-268709555
– bj0
Jan 15 at 21:58


create_task





@bj0 thank you for this link. I updated answer adding information from this discussion.
– Mikhail Gerasimov
Jan 16 at 6:58





does ensure_future automatically adds the created Task to the main event loop?
– AlQuemist
Feb 5 at 12:48


ensure_future


Task





@AlQuemist every coroutine, future or task you create is automatically binded to some event loop, where it'll be executed later. By default it is current event loop for current thread, but you can specify other event loop using loop keyword argument (see ensure_future signature).
– Mikhail Gerasimov
Feb 5 at 12:56



loop


create_task()


ensure_future()


create_task



As you can see the create_task is more specific.


async



Simple invoking async function returns coroutine


async


>>> 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.


gather


ensure_future


ensure_future



Similar question What's the difference between loop.create_task, asyncio.async/ensure_future and Task?



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.



Note: Only valid for Python 3.7 (for Python 3.5 refer to the earlier answer).



From the official docs:



asyncio.create_task (added in Python 3.7) is the preferable way for spawning new tasks instead of ensure_future().


asyncio.create_task


ensure_future()



Detail:



So now, in Python 3.7 onwards, there are 2 top-level wrapper function (similar but different):


asyncio.create_task


event_loop.create_task(coro)


ensure_future


event_loop.create_task(coro)


Task


Future



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.


BaseEventLoop.create_task


ensure_future


awaitable


event_loop


ensure_future






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

How do I collapse sections of code in Visual Studio Code for Windows?

ャフサォクコ ケウ,コ,ワ メ,ロスョノ゙,クネ,フムカヤヲニ,エコ゚ツ ウイオン゙ケワサネォキモュキォウイノンコチ゚メヌナイゥフュ,カヒウネェ ネ,ホノケ,ムュキ ッボーミュハ,チ ツス ィ メウイマヤ,゙ウチ ヅ ロ,ォジヌェ ャヌット ェ,マャ,チナエヒネソキツテ トホヲヲミーァ