DenaliHardtail
DenaliHardtail

Reputation: 28296

How do I get async code to wait until all tasks are complete?

When I run the code below, the message "Press Enter to continue... " appears before the results are returned from the HttpClient.GetAsync() calls are completed. The actual sequence of events: the GetAsync() calls are made, the "Press Enter..." message appears, and then the results are one-by-one output to the console window. How do I wait until all the GetAsync() calls are complete before displaying the "Press Enter..." message?

class Program
{
  static HttpClient client = new HttpClient();

  static void Main(string[] args)
  {
    RunAsync().Wait();

    Console.WriteLine("\n\n\n\nPress Enter to continue... ");
    Console.ReadLine();
  }

  static async Task RunAsync()
  {
    List<string> urls = new List<string>()
    {
      "http://www.domain1.com",
      "http://www.domain2.com",
      "http://www.domain3.com",
      "http://www.domain4.com"
    };

    foreach (var url in urls)
    {
      DownloadPageAsync(url);
    }
  }

  static async Task<string> DownloadPageAsync(string url)
  {
    Console.WriteLine("Starting: " + url);

    HttpResponseMessage response = await client.GetAsync(url);

    if (response.IsSuccessStatusCode)
    {
      // do stuff here
    }

    Console.WriteLine("Done: " + url);

    return response.Content.ToString();
  }
}

Upvotes: 0

Views: 1783

Answers (4)

Zinov
Zinov

Reputation: 4119

You need to create different tasks per every call you want, in your example you are running the code and not waiting for the call. When you call WhenAll that simply creates a task for all of them. Let's say that you use the code bellow and inside each MakeYouCall method you insert an item on a list. That list will be a shared resource that you need to lock. When the WhenAll is made, then if you don't wait for the result(make a call to Wait() ) then the collection could be partially filled.

var register1 = new Action(() => MakeYourCall1());
var register2 = new Action(() => MakeYourCall2());
var register3 = new Action(() => MakeYourCall3());

then

var t1 = Task.Factory.StartNew(register1);
var t2 = Task.Factory.StartNew(register2);
var t3 = Task.Factory.StartNew(register3);

after that you can call the WhenAll that will return a Task, then wait for it.

Task.WhenAll(t1, t2, t3).Wait();

Upvotes: -2

AndyPook
AndyPook

Reputation: 2889

The @patrik-hofman answer is a good one (up voted) although, see my comment

If you would rather the requests to happen sequentially... Add await to the DownloadPageAsync line.

You've used async in RunAsync but there are no awaits. So, although it returns a Task it is not waiting for the DownloadPageAsync call to complete. This means that the method simply returns an "empty" Task which completes immediately. So your .Wait() is waiting for nothing.

Upvotes: 1

Kevin Holditch
Kevin Holditch

Reputation: 5303

I think the problem is that you are not awaiting the DownloadPageAsync method inside the RunAsync() method. If you update the RunAsync() method to the code below then I believe it will work as you expect:

static async Task RunAsync()
{
    List<string> urls = new List<string>()
    {
      "http://www.domain1.com",
      "http://www.domain2.com",
      "http://www.domain3.com",
      "http://www.domain4.com"
    };

    foreach (var url in urls)
    {
        // added await here
        await DownloadPageAsync(url);
    }
}

Upvotes: 0

Patrick Hofman
Patrick Hofman

Reputation: 156898

Since DownloadPageAsync returns a task, you can make a list of all tasks and wait on them all:

Task.WhenAll(urls.Select(url => DownloadPageAsync(url)))

Or simplified:

Task.WhenAll(urls.Select(DownloadPageAsync))

Upvotes: 3

Related Questions