任务的OnCompleted回调在哪里?

时间:2016-08-31 01:13:17

标签: c# callback async-await task

我希望能够做到这一点:

class MyClass
{
    IRepo repo;
    public MyClass(IMyRepo repo) { this.repo = repo; }

    long ID { get; set; }
    string Prop1 { get; set; }
    string Prop2 { get; set; }

    public async Task LoadAsync()
    {
        await Task.WhenAll(
            repo.GetProp1ByIDAsync(ID).OnComplete(x => Prop1 = x),
            repo.GetProp2ByIDAsync(ID).OnComplete(x => Prop2 = x)
        );
    }
}

当然,我似乎无法找到OnComplete Task扩展名为ContinueWith的标准库。我真的需要创建自己的,还是已经有这种扩展方法的库?我看到那里有await但是这并没有给我打开包裹的结果,我仍然需要.Result它或repo它...所以不会那样阻止线程?举起第二个var display电话直到完成?如果它没有阻止它,那么为什么我仍然将这个结果包裹起来,将未打包的结果返回给我会更清楚吗?或者我错过了什么?

3 个答案:

答案 0 :(得分:2)

首先,您应该使用await Task.WhenAll(...)代替.WaitAll

ContinueWith是完成后启动另一项任务所需的。在传递给.Result的未包装结果上调用ContinueWith会在任务完成时立即返回,如果您等待它,也会一样。

在第一个任务上调用.ContinueWith对第二个任务没有影响,因为稍后使用await Task.WhenAll(...)等待它们,它们将在并行执行。

使用ReadFileAsync示例检查此link

答案 1 :(得分:2)

  

我似乎无法找到任何具有此OnComplete扩展名的任何标准库。我真的需要创建自己的,还是已经有这种扩展方法的库?我看到那里有ContinueWith但是这并没有给我打开的结果,我仍然需要等待它或.Result it ...所以不会阻止线程吗?

ContinueWith是您正在寻找的方法; Result不会阻止任务,因为在调用回调时它已经完成。

但是,ContinueWith是一个危险的低级API。您应该使用await代替:

public async Task LoadAsync()
{
    await Task.WhenAll(
        LoadProp1Async(),
        LoadProp2Async()
    );
}

private async Task LoadProp1Async()
{
  Prop1 = await repo.GetProp1ByIDAsync(ID);
}

private async Task LoadProp2Async()
{
  Prop2 = await repo.GetProp2ByIDAsync(ID);
}

答案 2 :(得分:1)

以下是您要寻找的扩展方法:

public static async Task OnCompletedSuccessfully(this Task task, Action continuation,
    bool continueOnCapturedContext = true)
{
    await task.ConfigureAwait(continueOnCapturedContext);
    continuation();
}

public static async Task OnCompletedSuccessfully(this Task task, Func<Task> continuation,
    bool continueOnCapturedContext = true)
{
    await task.ConfigureAwait(continueOnCapturedContext);
    await continuation().ConfigureAwait(false);
}

public static async Task OnCompletedSuccessfully<TResult>(
    this Task<TResult> task, Action<TResult> continuation,
    bool continueOnCapturedContext = true)
{
    var result = await task.ConfigureAwait(continueOnCapturedContext);
    continuation(result);
}

public static async Task OnCompletedSuccessfully<TResult>(
    this Task<TResult> task, Func<TResult, Task> continuation,
    bool continueOnCapturedContext = true)
{
    var result = await task.ConfigureAwait(continueOnCapturedContext);
    await continuation(result).ConfigureAwait(false);
}

public static async Task<TNewResult> OnCompletedSuccessfully<TResult, TNewResult>(
    this Task<TResult> task, Func<TResult, TNewResult> continuation,
    bool continueOnCapturedContext = true)
{
    var result = await task.ConfigureAwait(continueOnCapturedContext);
    return continuation(result);
}

public static async Task<TNewResult> OnCompletedSuccessfully<TResult, TNewResult>(
    this Task<TResult> task, Func<TResult, Task<TNewResult>> continuation,
    bool continueOnCapturedContext = true)
{
    var result = await task.ConfigureAwait(continueOnCapturedContext);
    return await continuation(result).ConfigureAwait(false);
}

我将OnCompleted重命名为OnCompletedSuccessfully,因为仅当任务成功完成时才会继续执行。好消息是,有许多重载可以理解异步委托,因此您无需拆开任何东西。坏消息是,在发生异常的情况下,您将获得异常,但是您将无法区分错误的任务和错误的延续。这远非理想。尤其是由于您打算将此方法链接到原始任务,并且可能没有指向原始任务的变量,因此您可以调查其属性。