How to use async within a lambda which returns a collection
- Convert your collection of
Thing
s into a collection ofTask<Thing>
s. - Then join all those tasks using
Task.WhenAll
and await it. - Awaiting the joint task will give you a
Thing[]
public async Task<JsonResult> GetLotsOfStuff()
{
IEnumerable<Task<ThingDetail>> tasks = collection.Select(q => GetDetailAboutTheThing(q.Id));
Task<int[]> jointTask = Task.WhenAll(tasks);
IEnumerable<ThingDetail> things = await jointTask;
return Json(things, JsonRequestBehavior.AllowGet);
}
Or, succinctly and using type inference:
public async Task<JsonResult> GetLotsOfStuff()
{
var tasks = collection.Select(q => GetDetailAboutTheThing(q.Id));
var things = await Task.WhenAll(tasks);
return Json(things, JsonRequestBehavior.AllowGet);
}
Fiddle: https://dotnetfiddle.net/78ApTI
Note: since GetDetailAboutTheThing
seems to return a Task<Thing>
, the convention is to append Async
to its name - GetDetailAboutTheThingAsync
.
Related videos on Youtube
Program.X
Full-stack developer, working on Microsoft SQL Server, .NET and through to JavaScript. Worked on web sites for NHS requiring high levels of accessibility, e-Commerce for leading media vendors, CRM and business solutions and FinTech.
Updated on June 19, 2022Comments
-
Program.X almost 2 years
I have a method which is Async "upstream". I'm trying to follow best practice and go all-in qith async all the way up the stack.
Within a Controller action within MVC I predictably hit the deadlock issue If I rely on .Result().
Changing the Controller action to async seems to be the way to go, though the issue is that the async method is called multiple times within a lambda.
How can I await on a lamda that returns multiple results?
public async Task<JsonResult> GetLotsOfStuff() { IEnumerable<ThingDetail> things= previouslyInitialisedCollection .Select(async q => await GetDetailAboutTheThing(q.Id))); return Json(result, JsonRequestBehavior.AllowGet); }
You can see I have tried making the lambda async, but this just gives a compiler exception:
Cannot convert source type
System.Collections.Generic.IEnumerable<System.Threading.Tasks.Task<ThingDetail>
to target typeSystem.Collections.Generic.IEnumerable<ThingDetail>
Where am I going wrong here?
-
Grundy about 9 yearsnow your select return enumeration with task, but you try assign it to enumeration with ThingDetail, as vatiant you can use
var
keyword instead declare type directly like:var things=...
instead ofIEnumerable<ThingDetail> things = ...
-
M.Babcock over 6 years
result
isn't defined in the code block. Presumably, you meant it to bereturn Json(things, JsonRequestBehavior.AllowGet)
?
-
-
Program.X about 9 years"That's the way to do it". Excellent, thanks. I had tried the WhenAll method but obviously had missed an interim step. I take your point about the Async method naming convention and I do agree. I'll implement this.
-
M.Babcock over 6 yearsShouldn't the last line in both code blocks be
return Json(things, JsonRequestBehavior.AllowGet);
?result
doesn't appear to be defined in either code block. -
dcastro over 6 years@M.Babcock Correct, it's fixed now. Thanks