I have the following code:
var result = MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
.ContinueWith((answer) =>
{
if (answer.Result == MessageBoxResult.Yes)
{
Task<bool> asyncTask = ExecuteAsyncFunc();
//asyncTask.Unwrap(); ??
asyncTask.ContinueWith((a) =>
{
// More
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
Invoked elsewhere like this:
public async Task<bool> ExecuteAsyncFunc()
{
await CallAnotherAsyncFunction();
}
I believe that I need to call Unwrap(), where I have tried to, because I am calling await inside a ContinueWith()
block. However, I get the following error when I uncomment it:
Error CS1929 'Task' does not contain a definition for 'Unwrap' and the best extension method overload 'TaskExtensions.Unwrap(Task)' requires a receiver of type 'Task'
Do I need to use Unwrap in this context, and if so, what am I doing wrong?
The short answer is to use
await
instead ofContinueWith
:The long answer is that
ContinueWith
has some dangerous default options (including using an ambientTaskScheduler
), andawait
automatically does everything correctly for you. I go into more detail on my blog.You say that you have to call unwrap due to your using await inside the
ContinueWith
; I don't actually see you using await though. I assume what you meant is that you want to await but are not sure how, and thus thought unwrapping is needed. If that is the case, then what you want is to just make your nested Task awaitable. You can do that by making the delegate you provideasync
This allows you to await within the delegate, within the ContinueWith call and not have to deal with unwrapping.
If you are going to do anything with the Task, such as return it without unwrapping, then this is the right way to go.
But if you are going to act on the results within the
Foo
method, then there is no need to use ContinueWith, just await it.That simplifies your code quiet a bit. The other thing to note is that your
ExecuteAsyncFunc()
method does not act on the return value. You simply await and do nothing else.I noticed that you're not returning true/false, so I assume there is more code there that you just omitted for clarity sake. If that isn't the case, you can save yourself some overhead and just return the Task, allowing someone further up the callstack to await it instead. If your call to
CallAnotherAsyncFunction
returnsTask<bool>
then you can just return that call in the same way as well. You only need to await if you have to prevent the method from going any further so you can react to the result of the awaited method. If you don't have to, just return the Task.You need to
Unwrap
only when your return type is aTask<Task>
, and you actually want to do something with the inner task.For example:
If I wanted to actually asynchronously wait on the inner
Task
, i'd callUnwrap
andawait
on that:If for any reason you'd want to
await
on any task returned from your continuation, or want to monitor the inner task, then you'd callUnwrap
. There is no need to do that here.What you're doing is simply invoking an async method inside your
ContinueWith
, but it doesn't seem like you want toawait
the result at all.I'd recommend using
async-await
wherever possible to reduce the verbosity ofContinueWith
. Mixing those two together usually yields bad results as thing get rather complicated. Refactoring that code ends up much cleaner with much less cyclomatic complexity: