TaskContinuationOptions OnlyOnCancelled 捕获未处理的错误

TaskContinuationOptions OnlyOnCancelled catches unhandled errors

我有以下代码来处理我的 TaskContinuations。我有点困惑,因为我有下面的 OnlyOnFaulted 块,如果任务抛出未处理的异常,我希望它会被输入。

但是,未处理的异常、使用 throw 重新抛出的已处理异常或取消将落在 OnlyOnCanceled 块中。

GetDataAsync(id).ContinueWith((antecedant) =>
{
    // do something when async method completed
    }, TaskContinuationOptions.OnlyOnRanToCompletion)
    .ContinueWith((antecedant) =>
    {
        var error = antecedant.Exception.Flatten(); //so when is this called if everything is cought by OnCancelled below?
    }, TaskContinuationOptions.OnlyOnFaulted)
    .ContinueWith((antecedant) =>
    {
        // this is fired if method throws an exception or if CancellationToken cancelled it or if unhandled exception cought
        var error = "Task has been cancelled";
    }, TaskContinuationOptions.OnlyOnCanceled);

我希望重新抛出的错误和取消将落在 OnlyOnCanceled 块中,而未处理的异常将落在 OnlyOnFaulted 块中

请注意,我不能只 await GetDataAsync,因为这是在从 View 的 c-tor 调用的方法中调用的。我解释说在这个 post

更新

我使用的是 Task.Run,而不是上面的代码。我正在用异步装饰传入 Task.Run 的 lambda,以提供 "Async all the way",正如 Jon Goldberger 在 https://blog.xamarin.com/getting-started-with-async-await/

推荐的那样
Task.Run(async() =>  
{
    try
    {
        IList<MyModel> models = await GetDataAsync(id);
        foreach (var model in models)
        {
            MyModelsObservableCollection.Add(model);
        }
    } catch (OperationCancelledException oce) {}
    } catch (Exception ex) {}

});

这感觉是一个更好的解决方案,因为我可以用 try...catch 块将代码包装在 Task.Run 中,并且异常处理的行为符合我的预期。

我绝对打算尝试 Stephen Cleary 在 https://msdn.microsoft.com/en-us/magazine/dn605875.aspx 上提出的建议,因为它看起来是一个更干净的解决方案。

正如我在其他回答中所说,您应该使用 await,并且由于这是 ViewModel 的构造函数,您应该 synchronously initialize to a "Loading..." state and asynchronously update that ViewModel to a "Display Data" state.

直接回答这个问题,问题是ContinueWithreturns一个任务代表延续,而不是前件。为了简化您问题中的代码:

GetDataAsync(id)
    .ContinueWith(A(), TaskContinuationOptions.OnlyOnRanToCompletion);
    .ContinueWith(B(), TaskContinuationOptions.OnlyOnFaulted)
    .ContinueWith(C(), TaskContinuationOptions.OnlyOnCanceled);
如果 GetDataAsync(id) 运行完成,将调用

A()B() 将在 A() 故障时被调用(注意:如果 GetDataAsync(id) 故障则不会)。如果 B() 被取消,C() 将被调用(注意:如果 GetDataAsync(id) 被取消则不会)。

您对 ContinueWith 的使用还有一些其他问题:它缺少一些标志(例如 DenyChildAttach),并且它使用的是当前的 TaskScheduler,这可能会导致令人惊讶的行为。 ContinueWith is an advanced, low-level method; use await instead.