Is result of Task.WhenAll order guaranteed?
Asked Answered
C

2

10

From the following test we can see the current version of framework guarantees the output order is the same that of as the input tasks.

async Task<string> GetString1()
{
    await Task.Delay(2000);
    return "1";
}

async Task<string> GetString2()
{
    await Task.Delay(1000);
    return "2";
}

var results = await Task.WhenAll(GetString1(), GetString2());
//now we have results[0] == "1" results[1] == "2"

However, from the documentation I can't find anything about this behavior, which means it's not document-guaranteed. From the opinions of answers in this question:

Do I need to put "order flags" in the output? e.g. change the example code into following:

class OrderTaskResult<T>
{
    public OrderTaskResult(int order, T value)
    {
        this.Order = order;
        this.Value = value;
    }
    public int Order { get; private set; }
    public T Value { get; private set; }
}

async Task<OrderTaskResult<string>> GetString1()
{
    await Task.Delay(2000);
    return new OrderTaskResult<string>(1, "1");
}
Cymatium answered 6/4, 2016 at 2:23 Comment(0)
K
20

You're looking at the documentation for the wrong overload.

If you look at the overload that actually returns the results, you'll see:

The Task<TResult>.Result property of the returned task will be set to an array containing all of the results of the supplied tasks in the same order as they were provided

Killer answered 6/4, 2016 at 2:29 Comment(0)
C
0

Answering your second question, though recognizing it is now moot in this particular case -

Q2: (maybe primary opinion-based) Is it really bad practice to code depending on undocumented behaviors, especially when some behaviors have little possibility to be changed? Sometimes you need to add lots of code to avoid undocumented behaviors.

Going to give the canonical engineering answer - it depends. You need to weigh the benefit of simpler code working for the present with the risk of having the rug yanked out from under your feet in the future.

On the one hand, "technical debt" is something you could maybe benefit from reading up on to build your intuition here -

Technical debt (also known as design debt or code debt, but can be also related to other technical endeavors) is a concept in software development that reflects the implied cost of additional rework caused by choosing an easy (limited) solution now instead of using a better approach that would take longer.

On the other hand, you can perhaps mitigate the risk to an acceptable degree by architecting your codebase in such a way that it is modular and loosely-coupled - something which, if you are following the guidance given by the SOLID design principles, you will likely be doing anyway. Or if you are taking a more functional route, you might try taking a bite out of Mark Seemann's Impureim sandwich, which also goes a long way to keeping your core domain logic decoupled from application details subject to change with the next hype or deprecation of a particular passing technology.

Commanding answered 21/10, 2021 at 12:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.