如何添加异步“;等待”;到addrangeselect语句

本文关键字:等待 语句 addrangeselect 异步 何添加 添加 | 更新日期: 2023-09-27 18:21:35

我有一个这样的函数:

public async Task<SomeViewModel> SampleFunction()
{
    var data = service.GetData();
    var myList = new List<SomeViewModel>();
    myList.AddRange(data.select(x => new SomeViewModel
    {
        Id = x.Id,
        DateCreated = x.DateCreated,
        Data = await service.GetSomeDataById(x.Id)
    }
    return myList;
}

我的await不起作用,因为它只能在用async修饰符标记的方法或lambda中使用。带此功能的async应该放在哪里?

如何添加异步“;等待”;到addrangeselect语句

只能在async方法/委托中使用await。在这种情况下,必须将该lambda表达式标记为async

但是等等,还有更多。。。

Select来自前async时代,因此它不处理async lambdas(在您的情况下,它将返回IEnumerable<Task<SomeViewModel>>,而不是您实际需要的IEnumerable<SomeViewModel>)。

但是,您可以自己添加该功能(最好是作为一种扩展方法),但您需要考虑是希望在继续下一个项目之前await每个项目(按顺序),还是希望在最后await所有项目(同时)。

顺序async

static async Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
    var results = new List<TResult>();
    foreach (var item in enumerable)
    {
        results.Add(await selector(item));
    }
    return results.ToArray();
}

并发async

static Task<TResult[]> SelectAsync<TItem, TResult>(this IEnumerable<TItem> enumerable, Func<TItem, Task<TResult>> selector)
{
    return Task.WhenAll(enumerable.Select(selector));
}

用法

public Task<SomeViewModel[]> SampleFunction()
{
    return service.GetData().SelectAsync(async x => new SomeViewModel
    {
        Id = x.Id,
        DateCreated = x.DateCreated,
        Data = await service.GetSomeDataById(x.Id)
    }
}

您在lambda内部使用await,该lambda将由编译器转换为其自己的单独命名方法。要使用await,它本身必须是async,而不仅仅是在async方法中定义的。当您生成lambda async时,您现在有了一系列任务,您希望将这些任务异步转换为它们的结果序列。Task.WhenAll正是这样做的,所以我们可以将我们的新查询传递给WhenAll,以获得表示我们的结果的任务,这正是该方法想要返回的:

public Task<SomeViewModel[]> SampleFunction()
{
    return Task.WhenAll(service.GetData().Select(
        async x => new SomeViewModel
    {
        Id = x.Id,
        DateCreated = x.DateCreated,
        Data = await service.GetSomeDataById(x.Id)
    }));
}

尽管对您的用例来说可能太重了,但使用TPL数据流将使您能够更好地控制异步处理。

public async Task<List<SomeViewModel>> SampleFunction()
{
    var data = service.GetData();
    var transformBlock = new TransformBlock<X, SomeViewModel>(
        async x => new SomeViewModel
        {
            Id = x.Id,
            DateCreated = x.DateCreated,
            Data = await service.GetSomeDataById(x.Id)
        },
        new ExecutionDataflowBlockOptions
        {
            // Let 8 "service.GetSomeDataById" calls run at once.
            MaxDegreeOfParallelism = 8
        });
    var result = new List<SomeViewModel>();
    var actionBlock = new ActionBlock<SomeViewModel>(
        vm => result.Add(vm));
    transformBlock.LinkTo(actionBlock,
        new DataflowLinkOptions { PropagateCompletion = true });
    foreach (var x in data)
    {
        transformBlock.Post(x);
    }
    transformBlock.Complete();
    await actionBlock.Completion;
    return result;
}

如果service.GetData()返回IObservable<X>,而此方法返回IObservable<SomeViewModel>,则这可能不会太冗长。