Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two tasks are firing even when there's only one item, using async/await and Task.WhenAll

What's wrong with my code here? Even when items.count is only 1, the DoSomething method gets called twice, and counter is equal to 2. Have I not structured the awaits correctly or am I using the WhenAll incorrectly?

public async Task<int> Process(string id)
    {
        var items = await GetItemsAsync(id);
        var counter = 0;

        var tasks = items.Select(async item =>
            {
                if (await DoSomething(item))
                    counter++
            });

            if (tasks.Count() > 0)
                await Task.WhenAll(tasks);

        return counter;
     } 
like image 956
Prabhu Avatar asked Dec 11 '22 04:12

Prabhu


1 Answers

You're using Select incorrectly, basically. It's lazy, remember? So every time you iterate over it (once for Count() and once in WhenAll)... it's going to call your delegate again.

The fix is simple: just materialize the query, e.g. with ToList():

var tasks = items.Select(async item =>
{
    if (await DoSomething(item))
    {
        counter++
    }
}).ToList();

That way you create the tasks once. In fact, tasks.Count() doesn't need to iterate at all now, as it's optimized to use the Count property. But I'd still use Any() instead:

if (tasks.Any())
{
    await Task.WhenAll(tasks);
}

(I'd strongly advise always using braces, by the way. It's much more bug-resistant...)

like image 107
Jon Skeet Avatar answered Feb 12 '23 09:02

Jon Skeet