You’ve got five API calls firing in parallel, and you want to process each result as soon as it lands instead of waiting for the slowest one to finish. Before .NET 9, this meant a gnarly while loop with Task.WhenAny, removing completed tasks from a list, and praying you didn’t mess up the bookkeeping. Task.WhenEach makes that entire pattern a one-liner.
Here’s the old way (functional, but ugly):
// The "before": nobody enjoyed writing thisvar tasks = new List<Task<string>>{ FetchAsync("https://api.example.com/users"), FetchAsync("https://api.example.com/orders"), FetchAsync("https://api.example.com/products"),};
while (tasks.Count > 0){ var completed = await Task.WhenAny(tasks); tasks.Remove(completed); Console.WriteLine(await completed);}And here’s the .NET 9 way:
using System.Net.Http;
var client = new HttpClient();
var tasks = new[]{ FetchAsync("https://jsonplaceholder.typicode.com/posts/1"), FetchAsync("https://jsonplaceholder.typicode.com/posts/2"), FetchAsync("https://jsonplaceholder.typicode.com/posts/3"),};
await foreach (var completed in Task.WhenEach(tasks)){ var result = await completed; // already done, returns instantly Console.WriteLine($"Got: {result[..Math.Min(80, result.Length)]}...");}
async Task<string> FetchAsync(string url){ await Task.Delay(Random.Shared.Next(100, 1000)); // simulate variable latency return await client.GetStringAsync(url);}Task.WhenEach returns an IAsyncEnumerable<Task<T>> that yields each task in the order it completes, not the order it was started. You just await foreach over it and handle results as they stream in. The code reads like English, and there’s zero manual bookkeeping.
This pattern is perfect for fan-out scenarios: calling multiple microservices, scraping multiple URLs, or running parallel database queries. Anywhere the response times vary and you want to start processing ASAP, this delivers. Combine it with a CancellationToken and you can even bail out early once you’ve got what you need.
No more WhenAny loops. No more mutable lists. Just await foreach and vibes.