- Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Let's throw another marginally useful idea for configuring awaits.
Background and Motivation
The await operator currently propagates only the first of the exceptions stored in a Task object. This is convenient in the vast majority of cases, because most asynchronous APIs by design will never complete with more than one errors. There is a handful of APIs though that do propagate multiple exceptions quite normally, and most of this information is lost when these APIs are awaited in the standard way. Although it is possible for a programmer to observe and handle all errors in this case, the workaround needed is slightly cumbersome and error prone.
Proposed API
A new value ThrowAggregateException for the AwaitBehavior enum, which serves as a parameter for the ConfigureAwait method. This is an extension of the #47525 proposal.
namespace System.Threading.Tasks { [Flags] public enum AwaitBehavior { //... ThrowAggregateException = 0x8, // Instead of throwing only the first exception } }Usage Examples
This configuration could be used when awaiting the Task.WhenAll method, the upcoming Parallel.ForEachAsync method, and the Completion property of the TPL Dataflow blocks:
try { await Task.WhenAll(tasks).ConfigureAwait(AwaitBehavior.ThrowAggregateException); } catch (AggregateException aex) { aex.Handle(ex => { /* ... */ }); }The current workaround is to store the task in a variable before awaiting it, and in case of
exception to query the task's Exception property:
var whenAllTask = Task.WhenAll(tasks); try { await whenAllTask; } catch (Exception ex) { if (whenAllTask.IsFaulted) { whenAllTask.Exception.Handle(ex => { /* ... */ }); } else { // handle ex, which is certainly an OperationCanceledException } }Alternative Designs
An extension method WithAggregateException has been suggested in a StackOverflow question:
public static async Task WithAggregateException(this Task source) { try { await source.ConfigureAwait(false); } catch { if (source.Exception == null) throw; ExceptionDispatchInfo.Capture(source.Exception).Throw(); } }Risks
None that I can think of.