asyncio gather scheduling order guarantee

From Python documentation

awaitable asyncio.gather(*aws, loop=None, return_exceptions=False)

Run awaitable objects in the aws sequence concurrently.

If any awaitable in aws is a coroutine, it is automatically scheduled as a Task.

If all awaitables are completed successfully, the result is an aggregate list of returned values. The order of result values corresponds to the order of awaitables in aws.

So the order of result values is preserved, but the execution order is not.


There is one interesting corner case where order breaks down at least in Python 3.6.

Let's take an example like this:

import asyncio

async def main():
    data = [1, 2, 3, 4, 5, 6, 7, 8]

    async def aiter(iterable):
        for v in iterable:
            yield v

    aiterable = aiter(data)
    aiterables = [aiterable] * 8

    values = await asyncio.gather(
        *[it.__anext__() for it in aiterables])
    
    assert values == data, f'{values} != {data}'

loop = asyncio.get_event_loop()
loop.run_until_complete(main())

Approximate result in Py36:

AssertionError: [7, 6, 3, 4, 5, 2, 8, 1] != [1, 2, 3, 4, 5, 6, 7, 8]

This behavior looks logical, since __anext__() coroutines are executed in arbitrary order. However, in Py37+ the code works fine and output list order is preserved. I didn't find any mention of this in the documentation.


Yes, at least from the source code of the cpython implementation, they will be scheduled in the order. The list of co-routines is iterated and a task added to the loop for each one of them one by one. Although I do not see in which scenario the scheduling guarantee becomes important. There is no guarantee the loop will execute them in order, nor any gurantee they will complete in that order. I think this will depend on the implementation details of the specific loop and the nature of the code. For example, try adding a asyncio.sleep(1) to the co-routine before the print.