Processing Tasks as They Complete [ Documentation]

Task.WhenAny allows for starting multiple tasks at the same time and processing them one by one in the order in which they complete instead of the order in which they are started.

static async Task SumPageSizesAsync()
{
    var stopwatch = Stopwatch.StartNew();

    IEnumerable<Task<int>> downloadTasksQuery = from url in s_urlList
                                                select ProcessUrlAsync(url, s_client);

    // Start each Task since LINQ uses deferred execution otherwise:
    List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

    int total = 0;

    while (downloadTasks.Any())
    {
        // Hold the next Task that finishes:
        Task<int> finishedTask = await Task.WhenAny(downloadTasks);

        downloadTasks.Remove(finishedTask); // Remove that Task from the list
        
        total += await finishedTask; // Unwrap the int from the Task, or, if faulted, throw the first child Exception
    }

    stopwatch.Stop();

    Console.WriteLine($"\nTotal bytes returned: {total:#,#}");
    Console.WriteLine($"Elapsed time: {stopwatch.Elapsed}\n");
}

See Also

https://devblogs.microsoft.com/pfxteam/processing-tasks-as-they-complete/