22

My understanding is that return Task.FromResult(foo) is a simple shorthand for:

var tcs = new TaskCompletionSource<TFoo>();
tcs.SetResult(foo);
return tcs.Task;

Is there some equivalent for a task that returns an exception state?

var tcs = new TaskCompletionSource<TFoo>();
tcs.SetException(new NotSupportedException()); // or whatever is appropriate
return tcs.Task;

I don't see anything like Task.FromException. Or would it be more appropriate to just throw the exception without returning a task?

Matt Johnson-Pint
  • 230,703
  • 74
  • 448
  • 575
  • 1
    Looking at your code makes me want to select it and use "Extract Method" in ReSharper. – John Saunders Mar 25 '14 at 00:50
  • 1
    It appears that such a method was intentionally not exposed: it exists, but is `internal`. – dlev Mar 25 '14 at 00:50
  • The trouble with that approach is that the exception you return wouldn't have a stack trace, since it is never actually thrown. Perhaps that's why the FromException is not exposed. – Thomas Levesque Mar 25 '14 at 01:14
  • @JohnSaunders - Sure, I can put it in a method, but I'm more interested if this is the right approach or not. – Matt Johnson-Pint Mar 25 '14 at 02:53
  • @ThomasLevesque - Hmmm.. so should I throw it, catch it and then return it in the Task? I don't think it should just be thrown. Or should it? – Matt Johnson-Pint Mar 25 '14 at 02:54
  • @MattJohnson, well, throwing it just to catch it doesn't seem a very good idea... I guess Noseratio's answer is a good solution – Thomas Levesque Mar 25 '14 at 08:38

2 Answers2

21

Starting with .NET 4.6 there is a Task.FromException method in the BCL.

There's also Task.FromCanceled.

Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
14

My understanding is that return Task.FromResult(foo) is a simple shorthand for... [TaskCompletionSource.SetResult].

Actually, Task.FromResult doesn't use TaskCompletionSource, its implementation is much simpler than that.

Is there some equivalent for a task that returns an exception state?

I reckon TaskCompletionSource would be the best option for this. You could also do something like this:

static Task FromExAsync(Exception ex) 
{
    var task = new Task(() => { throw ex; });
    task.RunSynchronously();
    return task;
}

The exception will not be propagated outside the returned task, until observed via await task or task.Wait(), which should be a desired behavior.

Note that if the exception passed to FromExAsync is an active exception (i.e. has been already thrown and caught elsewhere), re-throwing it like this would loose the current stack trace and Watson buckets information stored inside the exception. There are two ways of dealing with it:

  • Wrap the exception with AggregateException. This will make the original exception available as AggregateException.InnerException:

static Task FromExAsync(Exception ex) 
{
    var task = new Task(() => { throw new AggregateException(ex); });
    task.RunSynchronously();
    return task;
}
  • Use ExceptionDispatchInfo.Capture to flow the active exception's state:

static Task FromExAsync(Exception ex) 
{
    var ei = System.Runtime.ExceptionServices.ExceptionDispatchInfo.Capture(ex);
    var task = new Task(() => { ei.Throw(); });
    task.RunSynchronously();
    return task;
}

Finally, perhaps the simplest yet the worst option (due to the overhead of the state machine and the compiler warning) is to throw from an async method:

static async Task FromExAsync(Exception ex)
{
    throw ex;
}
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • Given this talk of overhead, perhaps one could consider using a custom Awaiter? – Aron Mar 25 '14 at 03:35
  • 1
    @Aron, I don't see how a custom awaiter would eliminate `async` and the task machine, as @MattJohnson needs a `Task` object for the return value. For some extra details, check [this](http://stackoverflow.com/q/22268569/1768303). – noseratio Mar 25 '14 at 03:39
  • 1
    Thanks. Async programming is very powerful, but sometimes even the smallest things get confusing. – Matt Johnson-Pint Mar 25 '14 at 04:26
  • 2
    Perhaps worth noting is that `Task` has an internal `FromException` function, and that it internally uses the exact same `Task.TrySetException` function that `TaskCompletionSource.TrySetException` also uses. That's a strong reason for me to use `TaskCompletionSource.TrySetException`: you get the exact result that MS thinks is a good idea for `Task.FromException`. –  Mar 12 '15 at 08:53
  • 6
    @hvd, there's new `Task.FromException` API in .NET 4.6: http://blogs.msdn.com/b/pfxteam/archive/2015/02/02/new-task-apis-in-net-4-6.aspx – noseratio Mar 12 '15 at 11:58
  • 1
    @Noseratio Heh, nice, thanks. I was looking at .NET 4.5, which does already have `FromException`, it just isn't public yet. –  Mar 12 '15 at 12:04