-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Configure an await to propagate all errors by throwing an AggregateException #47605
Comments
Tagging subscribers to this area: @tarekgh Issue DetailsLet's throw another marginally useful idea for configuring Background and MotivationThe Proposed APIA new value namespace System.Threading.Tasks
{
[Flags]
public enum AwaitBehavior
{
//...
ThrowAggregateException = 0x8, // Instead of throwing only the first exception
}
} Usage ExamplesThis configuration could be used when awaiting the 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 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 DesignsAn extension method public static async Task WithAggregateException(this Task source)
{
try
{
await source.ConfigureAwait(false);
}
catch
{
if (source.Exception == null) throw;
ExceptionDispatchInfo.Capture(source.Exception).Throw();
}
} RisksNone that I can think of.
|
CC @stephentoub |
There are certainly cases where it'd be helpful, but they're in the vast minority, and it wouldn't really save much code. We're talking about the difference between: await t.ConfigureAwait(AwaitBehavior.ThrowAggregateException); and await t.ConfigureAwait(AwaitBehavior.NoThrow);
t.Wait(); and if all you want is access to the exceptions, you don't even need that second line, just access t.Exception. Given the infrequency with which this would be needed and the trivial ability to get the behavior with one more line (if that), it doesn't seem warranted to pay the costs of building it in. |
Thanks for this workaround, it is good enough for me. The |
@stephentoub will the code above work with Or does it mean we need to first convert it to a Task? Where is it better to do it? // before the first await
Task<T> t = MethodAsync().AsTask();
await t.ConfigureAwait(AwaitBehavior.NoThrow);
t.Wait();
// just before Wait()
ValueTask<T> t = MethodAsync();
await t.ConfigureAwait(AwaitBehavior.NoThrow);
t.AsTask().Wait(); |
Technically, yes (with |
Hey @theodorzoulias, long time no no talk! :) |
Hi @noseratio! Thanks for pinging. I've seen your answer, and I've upvoted it a long time ago. It's a great solution, but Stephen Toub's approach is hard to beat! public static async Task WithAggregateException(this Task source)
{
try { await source.ConfigureAwait(false); } catch { source.Wait(); }
} |
Stephen's approach is simple and nice, I agree. The extension approach might be of interest to those who don't want to introduce another local variable for a |
@noseratio as you pointed out elsewhere, the Below is an improved version of this method, that hopefully works as expected. public static async Task WithAggregateException(this Task source)
{
try { await source.ConfigureAwait(false); }
catch (OperationCanceledException) when (source.IsCanceled) { throw; }
catch { source.Wait(); }
} |
@theodorzoulias this snippet is rather elegant but there's still one thing. In general, I'm not a fan of using E.g., if I know many would disagree, but these days, I prefer to not use |
@noseratio that's a controversial opinion! Anyway, in the future we'll have more incentives to use the |
I like that proposal about |
Let's throw another marginally useful idea for configuring
await
s.Background and Motivation
The
await
operator currently propagates only the first of the exceptions stored in aTask
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 areawait
ed 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 theAwaitBehavior
enum, which serves as a parameter for theConfigureAwait
method. This is an extension of the #47525 proposal.Usage Examples
This configuration could be used when awaiting the
Task.WhenAll
method, the upcomingParallel.ForEachAsync
method, and theCompletion
property of the TPL Dataflow blocks: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:Alternative Designs
An extension method
WithAggregateException
has been suggested in a StackOverflow question:Risks
None that I can think of.
The text was updated successfully, but these errors were encountered: