繁体   English   中英

如何返回任务 <T> 不用等待

[英]How to return a Task<T> without await

是否可以从一个方法返回任务,该方法首先调用多个Task<T>返回方法,然后返回某种包含先前调用结果的类型,而无需使用await

例如,以下内容很简单:

public Task<SomeType> GetAsync() => FirstOrDefaultAsync();

但是,我想做这样的事情:

public Task<SomeType> GetAsync()
{
    var list = GetListAsync();   // <-- Task<List<T>>
    var count = GetCountAsync(); // <-- Task<int>

    return new SomeType // <-- Obviously compiler error
    {
        List  /* <-- List<T> */ = list,  // <-- Also compiler error
        Count /* <-- int     */ = count, // <-- Also compiler error
    };
}

是否可以这样做而无需编写:

public async Task<SomeType> GetAsync()
{
    return new Type2
    {
        List = await GetListAsync(),
        Count = await GetCountAsync(),
    };
}

坦白说,问题中已经存在的版本是正确的

public async Task<SomeType> GetAsync()
{
    return new Type2
    {
        List = await GetListAsync(),
        Count = await GetCountAsync(),
    };
}

我知道您问过“不使用等待”,但是:避免等待的技巧不太理想 特别是, 几乎应该永远不要使用ContinueWith这是旧版API,并且Task实现现在已针对await进行了优化,而不是ContinueWith

至于:

因为我读到多次等待不利于性能。 我试图等待最后一次通话

没有; 一旦您有一个未完成的等待,您拥有多少便无所谓-他们实际上是免费的。 一个不完整的await与零个不完整的await可以与ContinueWith相提并论,因此:通过避免await ,您不会获得任何await

结论:只需使用await 它更简单,更直接,并且内部对此进行了优化。

作为次要优化,您可能要添加ConfigureAwait(false) ,即

public async Task<SomeType> GetAsync()
{
    return new Type2
    {
        List = await GetListAsync().ConfigureAwait(false),
        Count = await GetCountAsync().ConfigureAwait(false),
    };
}

或者,如果它们应同时运行,并且实现支持它:

public Task<SomeType> GetAsync()
{
    var list = GetListAsync(); 
    var count = GetCountAsync();

    return new SomeType
    {
        List = await list.ConfigureAwait(false),
        Count = await count.ConfigureAwait(false),
    };
}

您可以将Task.WhenAllTask.ContinueWith一起使用。

public Task<SomeType> GetAsync()
{
    var list = GetListAsync();
    var count = GetCountAsync();

    return Task.WhenAll(list, count).ContinueWith(_ => new Type2
    {
        List = list.Result,
        Count = count.Result,
    });
}

编辑

如评论中所建议,最好只使用await 我还建议阅读由GSerg链接的帖子 - 非异步方法与使用async / await的 Task.ContinueWith的 性能

问题在于Task.WhenAll方法不接受具有不同结果类型的任务。 所有任务必须是同一类型。 幸运的是,这很容易解决。 在以下情况下, WhenAll变体将等待两个具有不同类型的任务,并返回具有合并结果的任务。

public static Task<TResult> WhenAll<T1, T2, TResult>(
    Task<T1> task1, Task<T2> task2, Func<T1, T2, TResult> factory)
{
    return Task.WhenAll(task1, task2).ContinueWith(t =>
    {
        var tcs = new TaskCompletionSource<TResult>();
        if (t.IsFaulted)
        {
            tcs.SetException(t.Exception.InnerExceptions);
        }
        else if (t.IsCanceled)
        {
            tcs.SetCanceled();
        }
        else
        {
            tcs.SetResult(factory(task1.Result, task2.Result));
        }
        return tcs.Task;
    }, default, TaskContinuationOptions.ExecuteSynchronously,
        TaskScheduler.Default).Unwrap();
}

可以这样使用:

public Task<SomeType> GetAsync()
{
    return WhenAll(GetListAsync(), GetCountAsync(),
        (list, count) => new SomeType { List = list, Count = count });
}

与其他解决方案相比的优势在于处理异常。 如果GetListAsyncGetCountAsync失败,则从GetAsync返回的任务会将这两个异常都保留在浅AggregateException (不嵌套在另一个AggregateException )。

顺便说一句,这个答案是由Stephen Cleary 在这里的答案启发的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM