如何不等待就返回Task <T>

时间:2019-09-15 11:34:49

标签: c# async-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(),
    };
}

3 个答案:

答案 0 :(得分:7)

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

import App from './App';
require("mapbox-gl-js-mock");

jest.mock('mapbox-gl/dist/mapbox-gl', () => ({
  App: () => ({}),
}));

describe('<App />', () => {
  let shallow;

  beforeEach(() => {
    shallow = createShallow({ dive: true });
  });

  it('renders without crashing', () => {
    const wrapper = shallow(<App />);
    expect(wrapper.find('.MapBox')).toExist();
  });
});

我知道您问“不使用等待”,但是:避免等待的技巧是次佳;特别是,您应该几乎从不使用public async Task<SomeType> GetAsync() { return new Type2 { List = await GetListAsync(), Count = await GetCountAsync(), }; } -这是旧版API,并且ContinueWith实现现已针对Task(而非{{1})进行了优化}。

至:

  

因为我读到多次等待不利于性能。我尝试等待上次通话

否;一旦您有一个未完成等待,您拥有多少便几乎没有关系-他们实际上是免费的。不完整的await相对于零的问题与ContinueWith相当,因此:避免await并不会带来任何收益。

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

作为次要优化,您可能要添加await,即

await

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

ConfigureAwait(false)

答案 1 :(得分:0)

您可以将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-Performance of Task.ContinueWith in non-async method vs. using async/await

链接的帖子

答案 2 :(得分:0)

问题是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)的答案here