3

I am trying to understand await an async operation in asp.net MVC web api 2 client (console application). I believe I have done it wrong (due to lack of understanding await and async). It doesn't seem to run async. Here is code to understand the problem

//Main function

static void Main()
{
    RunAsync().Wait();

}

//RunAsync

static async Task RunAsync()
{
    using (var client = new HttpClient())
    {
        var apiUrl = ConfigurationManager.AppSettings["apiurl"];
        client.BaseAddress = new Uri(apiUrl);

        ....some code to fetch data

        foreach (var updateObject in updatedata)
        {
            HttpResponse  response = await client.PostAsJsonAsync("webapimethod", updateObject);
            if (response.IsSuccessStatusCode)
            {
                JArray content = await response.Content.ReadAsAsync<JArray>();
            }
        }
    }
}

In the above code, foreach loop I am making request in loop as PostAsJsonAsync call and then I use ReadAsAsync to get response back but request always runs sync. not like fired and then when response arrives read the data.

It works fine but I want it to be async and not waiting on each request. How to achieve that or please explain await async in this context? Trying to read blogs and articles but I don't get how it will work.

ekad
  • 14,436
  • 26
  • 44
  • 46
SSA
  • 5,433
  • 4
  • 36
  • 50
  • `Wait()` makes you block the thread until the async method has finished executing, thus making it synchronous. Use `await RunAsync()` instead – Alexander Derck Feb 18 '16 at 09:41
  • Okay I understand that but how about foreach loop ? – SSA Feb 18 '16 at 09:42
  • From your question it more sounds like you want to run the foreach parallel? Start the second one while the first one is waiting for `PostAsJsonAsync` etc.? – Alexander Derck Feb 18 '16 at 09:43
  • Yes exactly and what role await plays here? – SSA Feb 18 '16 at 09:43
  • 1
    @SSA here is question about async in foreach loop: http://stackoverflow.com/questions/19431494/how-to-use-await-in-a-loop – Volodymyr Usarskyy Feb 18 '16 at 09:44
  • 1
    @AlexanderDerck The `Wait()` is in the Main method of a console app. This is one of _very_ few cases where you do want to block an async call, otherwise the program will simply terminate immediately. I think this is actually a good structure for apps like this - put the meat of the program in an async method and do nothing but call it and block in Main. – Todd Menier Feb 18 '16 at 17:24

2 Answers2

3

The syntax you're probably looking for is this:

public async Task<JArray> GetContentAsync(... updateObject)
{
  HttpResponse  response = await client.PostAsJsonAsync("webapimethod", updateObject);
  if (response.IsSuccessStatusCode)
  {
      return await response.Content.ReadAsAsync<JArray>();
  }
}

Here your thread from the GetContentAsync() method will be put back on the threadpool while the client.PostAsJsonAsync is happening due to the await keyword.

Then you can create all the tasks in your method which calls it:

var updateData = fetchData();
var tasks = updateData.Select(d => GetContentAsync(d));
var result = (await Task.WhenAll(tasks)).ToList();

The Select will create a task for each of your result. The await Task.WhenAll will unwrap the Task<JArray> and create a List<JArray>

Alexander Derck
  • 13,818
  • 5
  • 54
  • 76
  • Thanks Alex, making more sense now. In List, Result is JArray right. One more question when await is used in foreach loop PostAsAsycJson, what blocks it and make it run synchronous? Is there any way to give every request a callback?In the code I have, instead of Task.WhenAll ? Just like we do ajax requests in loop in javascript. – SSA Feb 18 '16 at 09:56
  • Yes it would be `List`. In the foreach, you will release the thread because of the await, but it still won't go to the next loop of the foreach until everything is finished. The best thing to do is create a new `Task` for every item in your data and start those tasks. I do that with a select, but you could do it in the foreach too. Creating the task wouldn't wait until the tasks are finished like your foreach does at the moment. – Alexander Derck Feb 18 '16 at 10:04
  • Thanks for prompt reply and explanation with example. I have more insights now. – SSA Feb 18 '16 at 10:07
1

You have to move foreach out of RunAsync method. To get RunAsync in a foreach loop to work in async mode, you have to create several tasks and then call Task.WaitAll

Carlos Delgado
  • 316
  • 2
  • 11