Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need to wrap a python coroutine into a task/when to use a task or a coroutine?

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?

like image 970
Patrick Collins Avatar asked Dec 14 '22 07:12

Patrick Collins


1 Answers

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
like image 150
Shadows In Rain Avatar answered Jan 03 '23 11:01

Shadows In Rain