2

I'm not familiar with how exactly async/await work. To try to understand it better, I created a sample code below:

    static void Main(string[] args)
    {
        GetAPI();
        Console.WriteLine("Hello");
        Console.ReadLine();
    }

    public static async void GetAPI()
    {
        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Get", "application/json");

            var response = await client.GetAsync("http://somelinks");

            string content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
            Console.ReadLine();
        }
    }

The GetAPI() method will basically call a an API which return some content in Json format. However, the output that I receive is... surprising, despite the GetAPI() is being called first, "Hello" was being printed first in the Console. When I set the debugger, it seems to me right after it hits the await in the GetAPI(), it will go back to the Main.

How do I print the contents from the API first? In another word, how do I make sure the program finish executing everything in the GetAPI() first?

Additional info:

  1. I was forced to use async/await because HttpClient only provides the GetAsync method.
  2. I cannot use async/await in Main. It gives me an error saying Error 1 'ConsumeWebApi.Program.Main(string[])': an entry point cannot be marked with the 'async' modifier
6
  • Shouldn't you call await GetAPI() rather than GetAPI()? Why is GetAPI asynchronous at all, though? It calls async methods, but why is it async? Commented Apr 17, 2015 at 17:20
  • I cannot call await GetAPI() becaise it is in Main, you cannot use await in Main. Also, HttpClient only provides GetAsync method. Commented Apr 17, 2015 at 17:21
  • @AshleyFrieze Here is the error: Error 1 'ConsumeWebApi.Program.Main(string[])': an entry point cannot be marked with the 'async' modifier Commented Apr 17, 2015 at 17:26
  • 1
    Yeah. Try putting GetApi ().Wait () in main. It will sync things up I believe. Commented Apr 17, 2015 at 21:12
  • There is an oft repeated pearl of wisdom: "Never ever ever ever ever ever ever block async code. Except in the Main method of console apps." @AshleyFrieze is right - calling GetApi().Wait() from Main is all you need to do here. You should not change the internals of the GetApi method to meet the "special needs" of the console app, and for that reason I'd advise against the accepted answer. Commented Apr 18, 2015 at 13:52

4 Answers 4

3

You have a couple options both will give you the behavior you are looking for:

  • Change your GetAPI method to not be async, and call it like you are already

If your objective is simply to make this call sync so that your console.write print in order you can remove the async from the method declaration, not use await and do .result like so:

    private void LoadData()
    {
        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Accept.Add(
                new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));


            var response = client.GetAsync("http://somelinks").Result;

            string content = response.Content.ReadAsStringAsync().Result;
            Console.WriteLine(content);
            Console.ReadLine();
        }
    }

Also your http header to request json is incorrect you have a header named 'get'.

  • Leave your method as async and return a task and use .Wait() for it to complete when you call it from your console app.

If you want to make the call async you need to call it from an async method or use wait to wait for it to complete. Normally in an event handler you would just mark the event handler as async and make sure your return a type of Task from your async methods.

In a console program like your case you can wait for your async method. Your need to return a type Task from your async method not void like you are in order to be able to wait for it. This will allow you to wait for the async method to finish before completing and moving to the WriteLine. It will effectively be sync by doing this though and both approaches will behave the same in your console app.

 static void Main(string[] args)
 {
    Console.WriteLine("here");
    LoadData().Wait();
    Console.WriteLine("there");
 }

static async private Task LoadData()
{
    using (HttpClient client = new HttpClient())
    {
        client.DefaultRequestHeaders.Accept.Add(
            new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

        var response = await client.GetAsync("http://somelinks");

        string content = await response.Content.ReadAsStringAsync();
        Console.WriteLine(content);
        Console.ReadLine();
    }
}

In general it is best to leave a method async because if you move this to a shared library that is used by a program that can benefit from the method being async where you need it to be and sync where you need it to be like in your console app. You end up being able to do so.

Sign up to request clarification or add additional context in comments.

4 Comments

It's completely pointless to wrap the method call in a call to Run. It's doing nothing but wasting time.
If you agree, then why didn't you remove it?
This method is better left async. By blocking here, you're effectively coupling it to the console app. What if you wanted to move this method to a library and share it with a WPF app that could benefit from it being async? It's true you need to block somewhere in console app, but the correct place to do it is in Main. See Peter Dolkens' answer.
@ToddMenier I have added back in the part of my answer that spoke about leaving the method async (see my answer history) - and rephrased it. Hopefully it points out the points in a clear maner. Thanks
3

You should return Tasks from async methods I believe.

Then you can Wait for the return, like so:

    static void Main(string[] args)
    {
        GetAPI().Wait();
        Console.WriteLine("Hello");
        Console.ReadLine();
    }

    public static async Task GetAPI()
    {
        using (HttpClient client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add("Get", "application/json");
            var response = await client.GetAsync("http://google.com/");

            string content = await response.Content.ReadAsStringAsync();
            Console.WriteLine(content);
            Console.ReadLine();
        }
    }

2 Comments

That's actually incorrect as you don't await your async operation so program will end and terminate without the work being done.
@tchrikch It's not incorrect actually - he's calling .Wait() from Main. This is actually the most correct answer imo. You need to block somewhere in a console app to prevent it from exiting, and the generally accepted best place to do that is at the very top - the Main method.
0

The recommended answer to this is to use .Wait() on the async method when calling it from Main(). E.g.

// call this async method and wait for it to finish
GetApi().Wait();

// do something that should only happen when the async method is over

The await/async techniques create some background work that need not be synchronised until something does a Wait. Clearly the OP wants to wait for GetAPI to finish before proceeding, and Main methods probably don't want to exit with work still going on.

Comments

-1

I am still learning this (had this 2 weeks ago, but the lecturers told us that it is important that we use some controls if it works or not.

I don't know if it will help you out or not but I can try to write the code that we've been learned to use:

If you then implement it in your code and just change it to your needs, maybe it will work.

First of all your return type should always start with Task (if my lecturer is right)

public async static Task<List<string>> Test()
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(@"http://thebaseadres.net/api/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(
                    new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));

                HttpResponseMessage response = await client.GetAsync("lastbitofurl");
                if (response.IsSuccessStatusCode)
                {
                    string s = await response.Content.ReadAsStringAsync();
                    List<String> test = JsonConvert.DeserializeObject<List<string>>(s);
                    return test;
                }
                else
                    return null;
            }
        }

EDIT

You said you were forced to use async because of the Main thing, but can't you just enter your GetApi() method on mainpage_loaded?

Something like this:

    async void MainPage_Loaded(object sender, RoutedEventsArgs e)
    {
        await GetAPI();
    }

2 Comments

Oh there've been some edits and already some comments, sorry if this is a bad response..
Hmmm too bad.. Can't help you out then :(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.