【问题标题】:Where is the OnCompleted callback for Task?Task 的 OnCompleted 回调在哪里?
【发布时间】:2016-08-31 01:13:16
【问题描述】:

我希望能够做到这一点:

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

当然,我似乎找不到任何标准库具有 TaskOnComplete 扩展名。我真的需要创建自己的,还是有一个库已经有这个扩展方法?我看到有ContinueWith,但这并没有给我解开的结果,我仍然需要await 它或.Result 它......所以这不会阻塞线程吗?等待第二个repo 呼叫直到完成?如果它不支持它,那么为什么我仍然得到这个结果,将解开的结果返回给我会更干净吗?还是我错过了什么?

【问题讨论】:

  • 旁注:混合async 和回调可能会让读者感到困惑......我不会寻找库来实现一个语句功能,如:public static async Task OnComplete<T>(this Task<T> t, Action<T> callback){callback(await t);}

标签: c# callback async-await task


【解决方案1】:

我似乎找不到任何具有此 OnComplete Task 扩展的标准库。我真的需要创建自己的,还是有一个库已经有这个扩展方法?我看到有 ContinueWith 但这并没有给我解包的结果,我仍然需要等待它或 .Result 它......所以这不会阻塞线程吗?

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】:

    首先你应该选择await Task.WhenAll(...)而不是.WaitAll

    ContinueWith 是您在完成后启动另一个任务所需要的。在传递给ContinueWith 的解包结果上调用.Result 将立即返回,因为任务已经完成,如果您等待它也是如此。

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

    使用ReadFileAsync 示例检查此link

    【讨论】:

      【解决方案3】:

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

      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,因为只有在任务成功完成的情况下,延续才会运行。好消息是有可以理解异步委托的重载,所以你不需要解包任何东西。坏消息是,如果发生异常,您将恢复异常,但您将无法区分错误任务和错误延续。这远非理想。特别是因为您打算将此方法链接到原始任务,并且您可能没有指向原始任务的变量,以便您可以调查其属性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-01-13
        • 2020-04-26
        • 2017-04-06
        • 2019-08-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多