Closed
Description
EDITED on 1/23/2024 by @stephentoub:
namespace System.Threading.Tasks;
public class Task
{
+ public static IAsyncEnumerable<Task> WhenEach(params Task[] tasks);
+ public static IAsyncEnumerable<Task> WhenEach(params ReadOnlySpan<Task> tasks); // params when possible
+ public static IAsyncEnumerable<Task> WhenEach(IEnumerable<Task> tasks);
+ public static IAsyncEnumerable<Task<TResult>> WhenEach(params Task<TResult>[] tasks); // params for now; move it to ReadOnlySpan overload when that syntax is possible
+ public static IAsyncEnumerable<Task<TResult>> WhenEach(params ReadOnlySpan<Task<TResult>> tasks); // params when possible
+ public static IAsyncEnumerable<Task<TResult>> WhenEach(IEnumerable<Task<TResult>> tasks);
}
Background and motivation
Currently, if we need to "Process asynchronous tasks as they complete" then we need to write lots of unnecessary codes and its not straight forward, something like below.
// Using currently available APIs
List<Task<int>> downloadTasks = downloadTasksQuery.ToList();
while (downloadTasks.Any())
{
Task<int> finishedTask = await Task.WhenAny(downloadTasks);
downloadTasks.Remove(finishedTask);
Process(await finishedTask);
}
API Proposal
namespace System.Threading.Tasks
{
public class Task : IAsyncResult, IDisposable
{
public static IAsyncEnumerable<Task> WhenEach(params Task[] tasks); // Please change the name, if needed
public static IAsyncEnumerable<Task> WhenEach(IEnumerable<Task> tasks);
public static IAsyncEnumerable<Task<TResult>> WhenEach(params Task<TResult>[] tasks);
public static IAsyncEnumerable<Task<TResult>> WhenEach(IEnumerable<Task<TResult>> tasks);
}
}
API Usage
// Using newly created APIs
await foreach (var finishedTask in Task.WhenEach(downloadTasksQuery))
{
Process(await finishedTask);
}
Alternative Designs
No response
Risks
No response
Updates
(Others can edit this section and add more info)