Reputation: 6131
In python, there are 3 main types awaitable objects: coroutines, Tasks, and Futures.
I can await
a coroutine, and also a tasks
.
Awaiting a coroutine
import asyncio
async def nested():
return 42
async def main():
print(await nested()) # will print "42".
asyncio.run(main())
Awaiting a task
import asyncio
async def nested():
return 42
async def main():
task = asyncio.create_task(nested())
await task
asyncio.run(main())
What is the value of wrapping the coroutine in a task in the first place? It looks like they do the same thing.
When would I need to use a task vs a coroutine?
Upvotes: 8
Views: 3039
Reputation: 1220
Coroutine is just a function that runs in the context of current awaitable. It can yield execution to the event loop on behalf of the caller (the one who calls await
). Think of a function that is allowed to pause it's thread. You can call one coroutine from another, but they still share the same thread.
Task, on other hand, immediately posts a separate job to an event loop. The task itself is a handle to that job. You may await
a task, but it can run on itself just fine in "parallel" — in single threaded context this means that task can run while other josb are yielding (e.g. waiting for the I/O). Task may complete even before you call await
.
Example without tasks:
job_1 = sleep(5)
job_2 = sleep(2)
# will sleep for 5 seconds
await job_1
# will sleep for another 2 seconds
await job_2
Example with tasks:
job_1 = sleep(5)
job_2 = asyncio.create_task(sleep(2))
# will sleep for 5 seconds
await job_1
# by this time, job_2 is complete
# because previous job has yielded at some point, allowing other jobs to run
# thus await takes no time
await job_2
Upvotes: 13
Reputation: 42302
In this case there's no real difference: by awaiting the coroutine it's going to get scheduled as part of the task it's part of. However that means it's driven by its parent.
By wrapping a coroutine in a task, it gets independently scheduled on the event loop, meaning it is not driven by the containing task anymore (it has its own lifecycle) and it can be interacted with more richly (e.g. cancelled or have callbacks added to it).
Think "function" versus "thread", really. A coroutine is just a function which can be suspended (if it awaits stuff), but it still only exists within the lexical and dynamic context of its caller. A task is freed from that context, it makes the wrapped coroutine live its own life in the same way a thread makes the wrapped function (target) live its own life.
Upvotes: 6
Reputation: 62719
Creating a Task
schedules the passed coroutine to be run on an event loop. You can use the Task
to cancel the underlying coroutine.
Upvotes: 3