What actually happens when using async/await inside a LINQ statement?

前端 未结 2 595
一个人的身影
一个人的身影 2020-12-05 07:20

The following snippet compiles, but I\'d expect it to await the task result instead of giving me a List>.

var foo = bars.Sel         


        
相关标签:
2条回答
  • 2020-12-05 07:55

    does it have a certain use

    Sure. With async and await inside a LINQ statement you can e.g. do something like this:

    var tasks = foos.Select( async foo =>
        {
            var intermediate =  await DoSomethingAsync( foo );
            return await DoSomethingElseAsync( intermediate );
        } ).ToList();
    await Task.WhenAll(tasks);
    

    Without async/await inside a LINQ statement you're not awaiting anything inside the LINQ statement, so you can't process the result, or await for something else.

    Without async/await, in the LINQ statement you're only starting tasks, but not waiting for them to complete. They'll still complete eventually, but it'll happen long after the control will leave the LINQ statement, so you can only access their results after the WhenAll line will complete, but not inside the LINQ statement.

    0 讨论(0)
  • 2020-12-05 07:56

    I recommend that you not think of this as "using async within LINQ". Keep in mind what's in-between the two: delegates. Several LINQ operators take delegates, and async can be used to create an asynchronous delegate.

    So, when you have an asynchronous method BazAsync that returns a Task:

    Task BazAsync(TBar bar);
    

    then this code results in a sequence of tasks:

    IEnumerable<Task> tasks = bars.Select(bar => BazAsync(bar));
    

    Similarly, if you use async and await within the delegate, you're creating an asynchronous delegate that returns a Task:

    IEnumerable<Task> tasks = bars.Select(async bar => await BazAsync(bar));
    

    These two LINQ expressions are functionally equivalent. There are no important differences.

    Just like regular LINQ expressions, the IEnumerable<Task> is lazy-evaluated. Only, with asynchronous methods like BazAsync, you usually do not want accidental double-evaluation or anything like that. So, when you project to a sequence of tasks, it's usually a good idea to immediately reify the sequence. This calls BazAsync for all the elements in the source sequence, starting all the tasks going:

    Task[] tasks = bars.Select(bar => BazAsync(bar)).ToArray();
    

    Of course, all we've done with Select is start an asynchronous operation for each element. If you want to wait for them all to complete, then use Task.WhenAll:

    await Task.WhenAll(tasks);
    

    Most other LINQ operators do not work as cleanly with asynchronous delegates. Select is pretty straightforward: you're just starting an asynchronous operation for each element.

    0 讨论(0)
提交回复
热议问题