Wait on several SendMailAsync operations with aggregating exception handling in C#.

//
// Let's say you want to send email messages to several customers. You can overlap
// sending the messages so you're not waiting for one message to complete before
// sending the next. You can also find out when the send operations have completed
// and whether any errors have occurred:
IEnumerable<Task> asyncOps = from addr in addrs select SendMailAsync(addr);
try
{
    await Task.WhenAll(asyncOps);
}
catch(Exception exc)
{
    ...
}

// In this case, if any asynchronous operation fails, all the exceptions will be
// consolidated in an AggregateException exception, which is stored in the Task
// that is returned from the WhenAll method. However, only one of those exceptions
// is propagated by the await keyword. If you want to examine all the exceptions,
// you can rewrite the previous code as follows:
Task [] asyncOps = (from addr in addrs select SendMailAsync(addr)).ToArray();
try
{
    await Task.WhenAll(asyncOps);
}
catch(Exception exc)
{
    foreach(Task faulted in asyncOps.Where(t => t.IsFaulted))
    {
        ... // work with faulted and faulted.Exception
    }
}