Is there a nonblocking Task.WaitAll similar to Task.WhenAll, but not parallel?
I wrote this, but maybe it’s built-in?
public async Task<IEnumerable<T>> AwaitAllAsync<T>(IEnumerable<Task<T>> tasks)
{
List<T> result = new List<T>();
foreach(var task in tasks)
{
result.Add(await task);
}
return result;
}
I want to know if there is a built-in way of waiting for all tasks to complete in async, but a sequential way.
Consider this code:
public class SaveFooCommandHandler : ICommandHandler<SaveFooCommand>
{
private readonly IBusinessContext context;
public SaveFooCommandHandler(IBusinessContext context)
{
this.context = context;
}
public async Task Handle(SaveFooCommand command)
{
var foos = (await Task.WhenAll(command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id))).ToList()
...
}
}
That will fail, but
var foos = await context.AwaitAllAsync(command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id));
will not, context.FindAsync is an abstraction of dbcontext.Set<T>().FindAsync
You could do await context.Set<Foo>().Where(f => command.Foos.Contains(f.Id)).ToListAsync(), but the example is simplified.
I think the core misunderstanding is around the Task type. In asynchronous code, a Task is always already running. So this doesn't make sense:
Is there a non-blocking
Task.WaitAllsimilar toTask.WhenAllbut notparallelconcurrent?
If you have a collection of tasks, they're all already started.
I want to know if there is a build in way of waiting for all tasks to complete in async but sequential way.
You can, of course, await them sequentially. The standard pattern for this is to use await inside a foreach loop, just like the method you posted.
However, the only reason the sequential-await works is because your LINQ query is lazily evaluated. In particular, if you reify your task collection, it will fail. So this works:
var tasks = command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id));
var foos = await context.AwaitAllAsync(tasks);
and this fails:
var tasks = command.Foos.Select(foo => context.FindAsync<Foo>(foo.Id))
.ToList();
var foos = await context.AwaitAllAsync(tasks);
Internally, Task.WhenAll reifies your task sequence so it knows how many tasks it needs to wait for.
But this is really beside the point. The real problem you're trying to solve is how to serially execute asynchronous code, which is most easily done using foreach:
var foos = new List<Foo>();
foreach (var fooId in command.Foos.Select(f => f.Id))
foos.Add(await context.FindAsync<Foo>(fooId));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With