TaskContinuationOptions OnlyOnCancelled捕获未处理的错误

时间:2019-03-04 20:47:50

标签: c# async-await task continuations

我有以下代码来处理我的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调用的方法中调用的。我在这篇文章NetworkStream ReadAsync and WriteAsync hang infinitelly when using CancellationTokenSource - Deadlock Caused by Task.Result (or Task.Wait)

中解释了

更新

不是使用上面的代码,而是使用Task.Run,​​如下所示。我正在装饰传递给Task.Run的lambda,并按照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) {}

});

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

我绝对打算尝试在https://msdn.microsoft.com/en-us/magazine/dn605875.aspx上提供Stephen Cleary提出的建议,因为它似乎是一种更清洁的解决方案。

1 个答案:

答案 0 :(得分:0)

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

要直接回答这个问题,问题是ContinueWith返回的任务代表延续,而不是前任。为简化问题代码:

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

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

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