[英]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.WhenAll
与Task.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 });
}
与其他解决方案相比的优势在于处理异常。 如果GetListAsync
和GetCountAsync
失败,则从GetAsync
返回的任务会将这两个异常都保留在浅AggregateException
(不嵌套在另一个AggregateException
)。
顺便说一句,这个答案是由Stephen Cleary 在这里的答案启发的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.