ContinueWith 和 WhenAll 如何处理异常?

How to handle exceptions with ContinueWith and WhenAll?

我正在尝试异步加载多个文件并在每个文件加载完成后通知 UI,

_loadCancellationTokenSource = new CancellationTokenSource();

TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
var files = await picker.PickMultipleFilesAsync();
LoadedFiles.Clear();

loads = await Task.WhenAll(files.Select(file =>
{
    var load = LoadAsync(file);
    load.ContinueWith(t =>
    {
        if (t.IsCompleted) LoadedFiles.Add(file.Path);
        if (t.IsFaulted) NotifyUser(t.Exception.Message, NotifyType.ErrorMessage);
        if (t.IsCanceled) NotifyUser("operation was canceled.", NotifyType.ErrorMessage);
    }, scheduler);
    return load;
}));

private Task<Foo> LoadAsync(StorageFile file)
{
    // exception may be thrown inside load
    return Load(file, _loadCancellationTokenSource.Token);
}

问题是抛出异常时,没有处理。我知道为什么,因为 ContinueWith 创建了一个新任务,但我 return 正在执行旧任务。

这是因为ContinueWith是一个无效的任务。但我不知道如何正确 return 结果。我不确定使用 t.Result 是否安全,因为它可能会阻塞 UI 线程?


PS,我试过这段代码,但我得到 a task was cancelled exception 并且应用程序崩溃,即使我没有取消任何任务。加载某些文件时只会抛出一些异常。

load = (await Task.WhenAll(files.Select(file =>
{
    var load = LoadAsync(file);
    load.ContinueWith(t =>
    {
        if (t.IsFaulted) NotifyUser(t.Exception.Message, NotifyType.ErrorMessage);
        if (t.IsCanceled) NotifyUser("operation was canceled.", NotifyType.ErrorMessage);
    }, _loadCancellationTokenSource.Token, TaskContinuationOptions.NotOnRanToCompletion, scheduler);
    return load.ContinueWith(t =>
    {
        LoadedFiles.Add(file.Path);
        return (file, t.Result);
    }, _loadCancellationTokenSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler); ;
})));

感谢@Jimi,我能够通过查看任务状态来解决这个问题。

loads = (await Task.WhenAll(files.Select(file =>
{
    return LoadAsync(file).ContinueWith(t =>
    {
        if (t.IsFaulted) NotifyUser(t.Exception.Message, NotifyType.ErrorMessage);
        if (t.IsCanceled) NotifyUser("operation was canceled.", NotifyType.ErrorMessage);

        if (t.Status == TaskStatus.RanToCompletion)
        {
            LoadedFiles.Add(file.Path);
            return t.Result;
        }

        return null;
    }, scheduler);
}))).Where(x => x != null).ToArray();