Example of use:
var users = await usernames.SelectTaskResults(GetUserDetails, 4);
where GetUserDetails(string username)
is a method that calls HttpClient to access an API and returns a User object. The SelectTaskResults
returns the awaited tasks. At most, there will be 4 concurrent threads calling the API at the same time for this example.
Concerns:
I'm worried .Wait()
could cause a deadlock.
Code:
public static async Task<IEnumerable<TResult>> SelectTaskResults<TInput, TResult>(
this IEnumerable<TInput> source,
Func<TInput, Task<TResult>> taskFunc,
int degreesOfParallelism = 1,
bool throwFaulted = false,
CancellationToken cancellationToken = default(CancellationToken))
{
// Task.Run creates the task but it doesn't start executing immediately - for debugging
var tasks = source
.Select(input => Task.Run(() => taskFunc(input), cancellationToken))
.ToArray();
Parallel.ForEach(tasks,
new ParallelOptions { MaxDegreeOfParallelism = degreesOfParallelism }
, task =>
{
try
{
task.Wait(cancellationToken); // .Start() doesn't work for promise-like tasks
}
catch (Exception)
{
if (throwFaulted)
{
throw;
}
}
}
);
var output = await Task.WhenAll(tasks.Where(x=> !x.IsFaulted));
return output.AsEnumerable();
}