I’m reposting an useful old post by Stephen Toub.
ForEach loop doesn’t supports Async/Await operators. If you like the possibility to have a foreach loop that can run in parallel, and can also leverage async/await operators to improve the performances of your loop, you can use the following, very useful, extension method.
public static class Extensions
{
public static Task ForEachAsync<TSource, TResult>(
this IEnumerable<TSource> source,
Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor)
{
var oneAtATime = new SemaphoreSlim(initialCount:1, maxCount:1);
return Task.WhenAll(
from item in source
select ProcessAsync(item, taskSelector, resultProcessor, oneAtATime));
}
private static async Task ProcessAsync<TSource, TResult>(
TSource item,
Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor,
SemaphoreSlim oneAtATime)
{
TResult result = await taskSelector(item);
await oneAtATime.WaitAsync();
try { resultProcessor(item, result); }
finally { oneAtATime.Release(); }
}
}
Usage:
var collection = new List<object>();
await collection.ForEachAsync(
async obj => { ... loop code, containing the await operators and a return statement ... },
(obj, result) => { ...action to perform with the result of the operation... });
Example:
var client = new MyAsyncServiceClient();
var requestUrls = new List<string>();
var responseList = new List<Response>();
await requestUrls.ForEachAsync(
async requestUrl => {
var response = await client.ExecuteRequest(requestUrl);
return response;
},
(requestUrl, response) => {
responseList.Add(response);
}
);
You can find here the original article, and here an useful post that explains the reasons behind the architectural choices.
Thanks Stephen!