Await keyword is applied before the foreach keyword and not before the call to the async method - C#

Shervan360 1,661 Reputation points
2023-10-04T05:54:27.0933333+00:00

Hello,

I was completely confused.

What is the difference between await keyword is applied before the foreach keyword and not before the call to the async method? How does each work?

In what order are they executed?

Could you please explain to me in detail and step by step?

Thanks in advance

With yield and IAsyncEnumerable: (await before Foreach)

await01

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{

     public static class MyAsyncMethods
     {
          public static async IAsyncEnumerable<long?>
               GetPageLengthsWithIAsyncEnumerableAndYield(List<string> output, params string[] urls)
          {
               HttpClient client = new();
               foreach (var url in urls)
               {
                    output.Add($"Started request for {url}");
                    var httpMsg = await client.GetAsync($"http://{url}");
                    output.Add($"Finished request for {url}");
                    yield return httpMsg.Content.Headers.ContentLength;
               }
          }

     }
     internal class Program
     {
          static async Task<IEnumerable<string?>> MainMethodForIAsyncEnumerableAndYield()
          {
               List<string> output = new();
               IAsyncEnumerable<long?> list = MyAsyncMethods.
                    GetPageLengthsWithIAsyncEnumerableAndYield(output,
                    "google.com", "microsoft.com", "amazon.com");
               await foreach (long? len in list)
               {
                    output.Add($"Page length: {len}");
               }
               return output;
          }
          static void Main()
          {
               var result = MainMethodForIAsyncEnumerableAndYield();

               Console.WriteLine(string.Join('\n', result.Result));

          }
     }
}

Without yield and IAsyncEnumerable: (await in Foreach)

Screenshot 2023-10-03 225047

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{

     public static class MyAsyncMethods
     {
          public static async Task<IEnumerable<long?>>
                    GetPageLengths(List<string> output, params string[] urls)
          {
               List<long?> results = new List<long?>();
               HttpClient client = new HttpClient();
               foreach (string url in urls)
               {
                    output.Add($"Started request for {url}");
                    var httpMessage = await client.GetAsync($"http://{url}");
                    results.Add(httpMessage.Content.Headers.ContentLength);
                    output.Add($"Completed request for {url}");
               }
               return results;
          }
     }
     internal class Program
     {
          static async Task<IEnumerable<string?>> MainMethod()
          {
               List<string> output = new List<string>();
               foreach (long? len in await MyAsyncMethods.GetPageLengths(output,
               "apress.com", "microsoft.com", "amazon.com"))
               {
                    output.Add($"Page length: {len}");
               }
               return output;
          }
          static void Main()
          {
               var result = MainMethod();

               Console.WriteLine(string.Join('\n', result.Result));

          }
     }
}

Developer technologies | C#
{count} votes

1 answer

Sort by: Most helpful
  1. Anonymous
    2023-10-05T03:49:38.16+00:00

    Hi @Shervan360 , Welcome to Microsoft Q&A,

    When the await keyword is used in an asynchronous method, it tells the program to wait for the asynchronous operation to complete before continuing with the code below. In the two examples you provided, we'll see different ways of using it.

    Example 1: Using yield and IAsyncEnumerable, await before foreach

    In this example, we use yield and IAsyncEnumerable to process a series of URLs asynchronously. The await keyword is used before the foreach loop. Proceed as follows:

    1. MainMethodForIAsyncEnumerableAndYield is the entry point. It initializes an empty list named output and calls MyAsyncMethods.GetPageLengthsWithIAsyncEnumerableAndYield, passing in a set of URLs: "google.com", "microsoft.com", and "amazon.com".
    2. In GetPageLengthsWithIAsyncEnumerableAndYield, create an HttpClient instance and use a foreach loop to traverse the provided URL.
    3. For each URL, it uses await client.GetAsync($"http://{url}") to perform an asynchronous HTTP request. The await keyword indicates that the method will pause until the HTTP request is completed, making your code non-blocking.
    4. While the HTTP request is in progress, control is returned to the calling code, allowing other operations to execute in parallel.
    5. When the HTTP request is completed, the code following await continues to execute. It records that the request has completed and then returns the length of the HTTP content header asynchronously using yield return.
    6. MainMethodForIAsyncEnumerableAndYield waits for the results of IAsyncEnumerable<long?> and uses await foreach (long? len in list) to iterate these results. This loop also allows other operations to be performed in parallel while waiting for each result.
    7. Inside the loop, it adds the page length to the output list.
    8. Once all iterations are complete, the MainMethodForIAsyncEnumerableAndYield method returns an output list containing the recorded message and page length.
    9. In the Main method, you wait for MainMethodForIAsyncEnumerableAndYield() and print the result.

    Example 2: No yield and IAsyncEnumerable, await inside foreach

    This example also handles a sequence of URLs, but does not use yield or IAsyncEnumerable. Instead, it uses await inside a foreach loop:

    1. MainMethod is the entry point. It initializes an empty list named output and calls MyAsyncMethods.GetPageLengths, passing in a set of URLs: "apress.com", "microsoft.com", and "amazon.com".
    2. In GetPageLengths, create an HttpClient instance and use a foreach loop to iterate over the provided URL.
    3. For each URL, it uses await client.GetAsync($"http://{url}") to perform an asynchronous HTTP request. The await keyword indicates that the method will pause until the HTTP request is completed, making your code non-blocking.
    4. But unlike the first example, there is no yield return in this method. Instead, it adds the page length directly to the results list and logs the request completion.
    5. After the loop completes, it returns the results list.
    6. In MainMethod, you use foreach to iterate over the IEnumerable<long?> returned by MyAsyncMethods.GetPageLengths.
    7. Inside the loop, it adds the page length to the output list.
    8. Once all iterations are complete, the MainMethod method returns an output list containing the recorded message and page length.
    9. In the Main method, you wait for MainMethod() and print the result.

    In short, both examples use await to perform asynchronous HTTP requests. The main difference is that the first example uses yield and IAsyncEnumerable, allowing for more efficient streaming of results, while the second example collects all results into a list before processing them. Which method you choose to use depends on your specific needs and how you want to handle the results of asynchronous operations.

    Best Regards,

    Jiale


    If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment". 

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.