.NET 9 : Task.WhenEach

In .NET 9, a new method, Task.WhenEach, has been introduced to streamline asynchronous programming. This method allows you to process tasks as they complete, rather than waiting for all tasks to finish. This is particularly useful in scenarios where tasks have varying completion times and you want to act on each one as soon as it's done.

Step 1. Create a function named PrintWithDelay

async Task<int> PrintWithDelay(int delay)
{
    await Task.Delay(delay);
    return delay;
}

This code defines an asynchronous method named PrintWithDelay that takes an integer delay as input and returns an integer.

async Task<int>

  • async: This keyword indicates that the method is asynchronous, meaning it can yield execution to other tasks while waiting for I/O operations or other asynchronous operations to complete.
  • Task<int>: This specifies the return type of the method. It will return a Task object that, when awaited, will yield the integer result.

await Task.Delay(delay)

  • Task.Delay(delay): This creates a new task that will complete after the specified delay milliseconds.
  • await: This keyword pauses the execution of the current method until the Task.Delay task completes. While waiting, the thread can be used by other tasks.

return delay

  • Once the delay has elapsed, the method resumes execution and returns the original delay value.

Step 2. Create a list of tasks that will each execute the PrintWithDelay method with different delay values.

List<Task<int>> printTasks = [
    PrintWithDelay(4000),
    PrintWithDelay(6000),
    PrintWithDelay(2000)
];
  • This declares a list named printTasks to store tasks. Each task in this list will return an integer.
  • There are three calls to the PrintWithDelay method, each with a different delay value (4000, 6000, and 2000 milliseconds, respectively).
  • Each call returns a Task<int>, representing an asynchronous operation that will eventually return an integer.
  • These tasks are added to the printTasks list.

Step 3. Utilize Task.WhenEach in .NET 9.

Task.WhenEach yields an IAsyncEnumerable, allowing asynchronous processing of tasks as they complete.

await foreach (var task in Task.WhenEach(printTasks))
{
    Console.WriteLine(await task);
}

Task.WhenEach(printTasks)

  • This part takes a collection of Task<int> objects (stored in printTasks).
  • It returns an IAsyncEnumerable<Task<int>>. This enumerable represents a sequence of tasks that will complete over time.

await foreach (var task in Task.WhenEach(printTasks))

  • This is an asynchronous foreach loop that iterates over the IAsyncEnumerable returned by Task.WhenEach.
  • The await keyword signifies that the loop will pause execution until the next task in the sequence completes.

In short, the code does the following

  • Schedules Tasks: The printTasks are scheduled to run asynchronously.
  • Processes Completed Tasks: As each task completes, it is yielded from the Task.WhenEach enumerable.
  • Logs Task Completion: The await foreach loop iterates over these completed tasks, and for each one, it logs a message to the console, which includes the task's status and result.

Output

.NET 9 : Task.WhenEach

By leveraging Task.WhenEach, you can write more efficient and responsive asynchronous code in .NET 9.

Happy Coding!