Skip to content
Advertisement

Python asyncio, why doesn’t asyncio.FIRST_COMPLETED work as described?

Let’s say I have two functions which return similar results. I want to call both and take the results of whichever returns first. So lets say I have two async functions which wait 5 and 2 seconds like so:

import asyncio
from time import sleep


async def func_1():
    for i in range(5):
        print("func 1")
        sleep(1)
    return "slept for 5"


async def func_2():
    for i in range(2):
        print("func 2")
        sleep(1)
    return "slept for 2"

And I have a function which calls them like so:

async def competition():
    task2 = asyncio.create_task(func_1())
    task1 = asyncio.create_task(func_2())

    done, pending = await asyncio.wait([task1, task2], return_when=asyncio.FIRST_COMPLETED)
    print(len(pending))
    print(len(done))

As you see the competition function uses asyncio.wait to run the two tasks and returns the results based on the FIRST_COMPLETED function. The documentation defines FIRST_COMPLETED as:

The function will return when any future finishes or is cancelled.

Also, I run the competition function as shown bellow.

asyncio.run(competition())

However, it does not return when one task is done. It returns when both are done. Furthermore, the asyncio.wait() method is defined as a concurrent executer.

Run awaitable objects in the aws iterable concurrently and block until the condition specified by return_when.

However, that is not what happens. The tasks run in a single thread and one runs after the other. So the result that I get if I run the code is this:

func 1
func 1
func 1
func 1
func 1
func 2
func 2
0
2

Am I missing anything? Or have I misunderstood how it works?

FYI: I am using python 3.10.

Advertisement

Answer

You’ve been using time.sleep in your code. When you chage your time.sleep calls in your code to await asyncio.sleep(1), the results are:

func 1
func 2
func 1
func 2
func 1
1
1

I had some hard time wrapping my head around how async programming works, but in my understanding asyncio is by design supposed to run everything in a single thread (cooperative multithreading). It pauses the execution of a given function, when it encounters a call such as await asyncio.sleep(1) and moves on to execute other functions that are scheduled for execution. The ordinary time.sleep does not allow asyncio to pause the execution.

User contributions licensed under: CC BY-SA
1 People found this is helpful
Advertisement