2015-06-29 20 views
4

我有以下代碼:使用等候ContinueWith內()塊

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

我相信,我需要調用展開(),在那裏我有試圖,因爲我打電話在ContinueWith()區塊內等待。不過,我得到以下錯誤,當我取消它:

錯誤CS1929「任務」不包含「展開」 的定義和最佳推廣方法重載 「TaskExtensions.Unwrap(任務)」需要接收器類型 '任務'

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

+3

你爲什麼要用'await'混合'ContinueWith'? –

+0

有關使用ContinueWith而不是等待的各種問題的答案是我來自同步代碼(遊戲)。 ContinueWith似乎是從同步調用異步函數的簡單方法。 –

回答

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自動做的一切正確地爲你。我在我的博客上更詳細地介紹。

+0

你在這裏(和你的文章中)所說的含義是,當前的TaskScheduler是明確指定的,上面的代碼應該沒問題。這是你的理解,還是有另一個不使用ContinueWith的理由? –

+0

@ pm_2:'ContinueWith'強迫你使用CPS(延續傳球風格),這比'await'更笨拙。你爲什麼要使用'ContinueWith'而不是'await'? –

+0

因爲調用代碼是一個遊戲循環。我無法停止主循環的執行,所以這似乎是最簡單的方法。 –

2

你說你必須打電話解包,因爲你在ContinueWith內使用等待;儘管如此,我實際上並沒有看到你在等待。我假設你的意思是你想等待,但不知道如何,因此認爲解包是必要的。如果是這種情況,那麼你想要的只是讓你的嵌套任務等待。你可以做到這一點通過將委託你提供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,所以我假設你有更多的代碼,爲了清楚起見你省略了。如果不是這種情況,你可以節省一些開銷,並返回任務,允許有人進一步調用callstack來等待它。如果您撥打CallAnotherAsyncFunction的電話返回Task<bool>,那麼您可以以相同的方式返回該電話。你只需要等待,如果你不得不阻止該方法進一步發展,那麼你可以對等待的方法的結果做出反應。如果你不需要,只需返回任務。

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

我需要在這方面使用展開,如果是這樣,我在做什麼 錯了嗎?

您需要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(); 
}