简体   繁体   中英

Is it possible to return a list of async items in an async method from an async source?

Essentially I'm trying to be able to do this:

var thingTasks = thingFactory.GetMyThings();
// ...
var things = await thingTasks;

I'm trying to start from a list of objects, iterate through that list making an async call for each one, and returning the set of results in an await -able way so the consumer can choose when to await it. GetMyThings itself uses await before generating the list, so it needs to be async itself and is something like:

public async Task<List<Thing>> GetMyThings() {
    var thingMakers = await GetThingMakers();
    var things = thingMakers.Select(async thing => await thing.GetAsync());
    return things;
}

The basic idea is that I have some await lines, then after that I use the results of those lines to generate a list and generating each item also requires an async call. I'm trying to avoid blocking within the method (eg .Result ) and instead pass that responsibility/opportunity back to the caller. Basically, start the tasks in the list but not await them. This naturally makes me want to return Task<List<Thing>> or 'List>`.

The closest I got was return Task.WhenAll(things) but that didn't work (it needed to be Task<Task<Thing[]>> and await await GetMyThings() . Alternatively, return Select(...) returning a Task<List<Task<Thing>>> and needing a await Task.WhenAll(await GetMyThings()) on the consuming side.

In both cases one needs double await statements to realize the list. I'm thinking it's impossible but is there a way to avoid the double await ?

Use Task.WhenAll to await all task at once. This way you will run each GetAsync approximately at the same time. So :

  1. Start all task
  2. Await all
  3. Return task's results

Like this :

public async Task<List<Thing>> GetMyThings()
{
    var thingMakers = await GetThingMakers();
    var tasks = thingMakers.Select(thing => thing.GetAsync());
    var things = await Task.WhenAll(tasks);
    return things.ToList();
}

If you want to make the inner tasks await-able outside, you need to actually return them:

public async Task<List<Task<Thing>>> GetMyThings() {
    var thingMakers = await GetThingMakers();
    var things = thingMakers.Select(thing => thing.GetAsync());
    return things.ToList();
}

You can then use this call like this:

List<Task<Thing>> thingTasks = await GetMyThings();
await Task.WhenAll(thingTasks);
List<Thing> = thingTasks.Select(t => t.Result).ToList();

Or even:

List<Thing> things = await GetMyThings()
    .ContinueWith(async r => 
        {
            await Task.WhenAll(r);
            return r.Select(r => r.Result).ToList();
        });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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