What's the difference between loop.create_task, asyncio.async/ensure_future and Task?
Question:
I’m a little bit confused by some asyncio
functions. I see there is BaseEventLoop.create_task(coro)
function to schedule a co-routine. The documentation for create_task
says its a new function and for compatibility we should use asyncio.async(coro)
which by referring to docs again I see is an alias for asyncio.ensure_future(coro)
which again schedules the execution of a co-routine.
Meanwhile, I’ve been using Task(coro)
for scheduling co-routine execution and that too seems to be working fine. so, what’s the difference between all these?
Answers:
As you’ve noticed, they all do the same thing.
asyncio.async
had to be replaced with asyncio.ensure_future
because in Python >= 3.5, async
has been made a keyword[1].
create_task
‘s raison d’etre[2]:
Third-party event loops can use their own subclass of Task for interoperability. In this case, the result type is a subclass of Task.
And this also means you should not create a Task
directly, because different event loops might have different ways of creating a “Task”.
Edit
Another important difference is that in addition to accepting coroutines, ensure_future
also accepts any awaitable object; create_task
on the other hand just accepts coroutines.
Future
A Future
is an object that is supposed to have a result in the future.
Or more officially:
A Future
is an awaitable
object that represents an eventual result of an asynchronous operation.
Object is awaitable
if it can be used in an await
expression.
So, any Future
can be await
‘ed.
Task
Tasks
are used to schedule coroutines concurrently.
When a coroutine is wrapped into a Task
with functions like
asyncio.create_task()
the coroutine is automatically scheduled to run
soon.
A Task actually is a Future:
A Future
-like object that runs a Python coroutine.
Task
is subclass of a Future
.
asyncio.Task
inherits from Future
all of its APIs except
Future.set_result()
and Future.set_exception()
.
create_task(coro)
Wraps the coro
coroutine into a Task
and schedules its execution.
Returns the Task
object.
asyncio.ensure_future(obj, …)
It actually ensures that obj
is a Future
. If it’s not it creates a Task
from obj
. That’s it.
Some conclusions
- If
obj
already is a Future
, ensure_future
will return the same obj
. In this case return value of a function may be a Future and not a Task – since Task
is just a subclass of a Future
.
- With
ensure_future
you CAN do this:
# will create a Task
task = asyncio.ensure_future(coroutine())
# will ensure that it is a Task
task = asyncio.ensure_future(task)
or this:
# will create a Task
task = asyncio.create_task(coroutine())
# will ensure that it is a Task
task = asyncio.ensure_future(task)
So, you can call ensure_future
on a Task
.
But you CAN’T do the same with create_task
:
# will create a Task
task = asyncio.create_task(coroutine())
# will raise an exception
task = asyncio.create_task(task)
TypeError: a coroutine was expected, got Task
Pay attention
create_task()
is the preferred way for creating new Tasks
.
Deprecated since version 3.10: Deprecation warning is emitted if obj
is not a Future-like object and loop is not specified and there is no
running event loop.
I’m a little bit confused by some asyncio
functions. I see there is BaseEventLoop.create_task(coro)
function to schedule a co-routine. The documentation for create_task
says its a new function and for compatibility we should use asyncio.async(coro)
which by referring to docs again I see is an alias for asyncio.ensure_future(coro)
which again schedules the execution of a co-routine.
Meanwhile, I’ve been using Task(coro)
for scheduling co-routine execution and that too seems to be working fine. so, what’s the difference between all these?
As you’ve noticed, they all do the same thing.
asyncio.async
had to be replaced with asyncio.ensure_future
because in Python >= 3.5, async
has been made a keyword[1].
create_task
‘s raison d’etre[2]:
Third-party event loops can use their own subclass of Task for interoperability. In this case, the result type is a subclass of Task.
And this also means you should not create a Task
directly, because different event loops might have different ways of creating a “Task”.
Edit
Another important difference is that in addition to accepting coroutines, ensure_future
also accepts any awaitable object; create_task
on the other hand just accepts coroutines.
Future
A Future
is an object that is supposed to have a result in the future.
Or more officially:
A Future
is an awaitable
object that represents an eventual result of an asynchronous operation.
Object is
awaitable
if it can be used in anawait
expression.
So, any Future
can be await
‘ed.
Task
Tasks
are used to schedule coroutines concurrently.When a coroutine is wrapped into a
Task
with functions like
asyncio.create_task()
the coroutine is automatically scheduled to run
soon.
A Task actually is a Future:
A
Future
-like object that runs a Python coroutine.
Task
is subclass of a Future
.
asyncio.Task
inherits fromFuture
all of its APIs except
Future.set_result()
andFuture.set_exception()
.
create_task(coro)
Wraps the
coro
coroutine into aTask
and schedules its execution.
Returns theTask
object.
asyncio.ensure_future(obj, …)
It actually ensures that obj
is a Future
. If it’s not it creates a Task
from obj
. That’s it.
Some conclusions
- If
obj
already is aFuture
,ensure_future
will return the sameobj
. In this case return value of a function may be a Future and not a Task – sinceTask
is just a subclass of aFuture
. - With
ensure_future
you CAN do this:
# will create a Task
task = asyncio.ensure_future(coroutine())
# will ensure that it is a Task
task = asyncio.ensure_future(task)
or this:
# will create a Task
task = asyncio.create_task(coroutine())
# will ensure that it is a Task
task = asyncio.ensure_future(task)
So, you can call ensure_future
on a Task
.
But you CAN’T do the same with create_task
:
# will create a Task
task = asyncio.create_task(coroutine())
# will raise an exception
task = asyncio.create_task(task)
TypeError: a coroutine was expected, got Task
Pay attention
create_task()
is the preferred way for creating newTasks
.
Deprecated since version 3.10: Deprecation warning is emitted if obj
is not a Future-like object and loop is not specified and there is no
running event loop.