Calling async method in IEnumerable.Select

You can work this out by considering the types in play. For example, MapToLocalObject – when viewed as an asynchronous function – does map from R to L. But if you view it as a synchronous function, it maps from R to Task<L>.

Task is a “future”, so Task<L> can be thought of as a type that will produce an L at some point in the future.

So you can easily convert from a sequence of R to a sequence of Task<L>:

IEnumerable<Task<L>> mappingTasks = remoteItems.Select(remoteItem => MapToLocalObject(remoteItem));

Note that there is an important semantic difference between this and your original code. Your original code waits for each object to be mapped before proceeding to the next object; this code will start all mappings concurrently.

Your result is a sequence of tasks – a sequence of future L results. To work with sequences of tasks, there are a few common operations. Task.WhenAll and Task.WhenAny are built-in operations for the most common requirements. If you want to wait until all mappings have completed, you can do:

L[] mappedItems = await Task.WhenAll(mappingTasks);

If you prefer to handle each item as it completes, you can use OrderByCompletion from my AsyncEx library:

Task<L>[] orderedMappingTasks = mappingTasks.OrderByCompletion();
foreach (var task in orderedMappingTasks)
{
  var mappedItem = await task;
  ...
}

Leave a Comment