在ContinueWith()块中使用await

时间:2015-06-29 17:34:13

标签: c# asynchronous async-await task-parallel-library task

我有以下代码:

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);
}

像这样在其他地方调用:

public async Task<bool> ExecuteAsyncFunc()
{
    await CallAnotherAsyncFunction();
}

我相信我需要调用Unwrap(),我试过这个,因为我在ContinueWith()块内调用await。但是,当我取消注释时,我收到以下错误:

  

错误CS1929&#39;任务&#39;不包含&#39; Unwrap&#39;的定义   和最好的扩展方法重载   &#39; TaskExtensions.Unwrap(任务)&#39;需要一个类型的接收器   &#39;任务&#39;

我是否需要在此上下文中使用Unwrap,如果是,我做错了什么?

3 个答案:

答案 0 :(得分:4)

简短的回答是使用await代替ContinueWith

var result = MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);
if (answer == MessageBoxResult.Yes)
{
  var a = await ExecuteAsyncFunc();
  // More 
}

答案很长,ContinueWith has some dangerous default options (including using an ambient TaskScheduler)await会自动为您正确执行所有操作。我在博客上详细介绍了。

答案 1 :(得分:3)

  

我是否需要在此上下文中使用Unwrap,如果是,我在做什么   错?

只有当您的返回类型为Unwrap时才需要Task<Task>,并且您实际上想要对内部任务执行某些操作。

例如:

Task<Task> exampleTask = Task.Factory.StartNew(async () => 
{
   await Task.Delay(1000); 
});

如果我想实际上异步等待内部Task,我会打电话给Unwrapawait

Task exampleTask = await Task.Factory.StartNew(async () => 
{
   await Task.Delay(1000); 
}).Unwrap();

如果出于任何原因,您希望await从您的续约中返回任何任务,或者想要监控内部任务,那么您可以致电Unwrap。这里没有必要这样做。

您正在做的只是在ContinueWith中调用异步方法,但似乎您根本不​​想await结果。

我建议尽可能使用async-await来减少ContinueWith的详细程度。将这两者混合在一起通常会产生不良结果,因为事情变得相当复杂。重构该代码的结果更清晰,更少的圈复杂度:

var result = await MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed",
                                                      MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
    bool answer = await ExecuteFuncAsync();
}

答案 2 :(得分:2)

你说由于你在ContinueWith内使用await,你必须打电话给unwrap;我实际上并没有看到你使用等待。我认为你的意思是你想等待但不确定如何,因此需要解开思考。如果是这种情况,那么你想要的只是使你的嵌套任务等待。您可以通过提供您提供的代理async

来实现
var result = MessageBoxHelper.MsgBox
    .ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
    .ContinueWith(async (answer) =>
    {
        if (answer.Result == MessageBoxResult.Yes)
        {
            await ExecuteAsyncFunc();
        }
    }, TaskContinuationOptions.OnlyOnRanToCompletion);

public async Task<bool> ExecuteAsyncFunc()
{
    await CallAnotherAsyncFunction();
}

这允许您在委托中等待,在ContinueWith调用内,而不必处理解包。

如果您要对任务执行任何操作,例如在不展开的情况下将其返回,那么这是正确的方法。

public Task<BoolOrSomeOtherReturnType> Foo()
{
    return MessageBoxHelper.MsgBox
    .ShowAsync /* Continue with etc here */
}

但是如果你要在Foo方法中对结果采取行动,那么就没有必要使用ContinueWith,只需等待它。

public async Task<bool> Foo()
{
    var result = await MessageBoxHelper.MsgBox
        .ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);

    if (result == MessageBoxResult.Yes)
    {
        await ExecuteAsyncFunc();
        return true;
    }

    return false;
}

这简化了您的代码安静。另外需要注意的是,ExecuteAsyncFunc()方法不会对返回值起作用。你只需等待,别无所求。

我注意到你没有返回true / false,所以我假设有更多的代码,为了清楚起见,你只是省略了。如果不是这种情况,您可以节省一些开销,只需返回任务,允许有人进一步向上调用它来代替它。如果您对CallAnotherAsyncFunction的呼叫返回Task<bool>,那么您也可以以同样的方式返回该呼叫。您只需要等待,如果您必须阻止该方法继续进行,以便您可以对等待方法的结果作出反应。如果您不必,只需返回任务。

public Task<bool> ExecuteAsyncFunc()
{
    return CallAnotherAsyncFunction();
}