0

I'm implementing code from a developer that are using async functions.

Example:

public async Task<string> GetDataAsync(string jsonString = "")
{
    string url = $"{this.url}";

    using (HttpClient httpClient = new HttpClient())
    {
        using (StringContent body = new StringContent(jsonString, Encoding.UTF8, "application/json"))
        {
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", this.accessToken);

            using (HttpResponseMessage response = await httpClient.PostAsync(url, body))
            {
                switch (response.IsSuccessStatusCode)
                {
                    case true:

                        return await response.Content.ReadAsStringAsync();

                    default:

                        throw new Exception($"Error calling url: {url}. HTTP status: {response.StatusCode}");

                }
            }
        }
    }
}

And I don't need, nor do I want, to call anything asynchronously. But the problem is that async functions is bubbling all the way up through my functions and so I can't just stop using it, and since HttpClient doesn't have a Post() function to use instead of PostAnync() then I feel stuck in this asynchronous cage.

Is there a trick or whatever to call an async function normally, stopping threading from bubbling up through all parent functions?

Or is the only solution to find a package without async functions?

CodeNotFound
  • 22,153
  • 10
  • 68
  • 69
Espen
  • 3,607
  • 11
  • 48
  • 75
  • 2
    `Task.Run(GetDataAsync).Result` might be what you are looking for. It waits untill the async-call is finished. – gerb0n May 02 '18 at 08:11
  • Aren't you blocking on async code then, which could lead you to a deadlock ? – Frederik Gheysels May 02 '18 at 08:13
  • 4
    The real question here is: why do you feel that you do not need / or do not want to use async ? When you want to create scalable applications, you will need to do that – Frederik Gheysels May 02 '18 at 08:14
  • `GetDataAsync("blah").GetAwaiter().GetResult()` is another option however it can deadliock, so you need to understand what you are doing, try reading this http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html – TheGeneral May 02 '18 at 08:18
  • @GVersluis Like it. Now I just need to find out how to run it with arguments. – Espen May 02 '18 at 08:21
  • Please take into account the warnings about the deadlocks mentionned here, before shooting yourself in the foot. – Frederik Gheysels May 02 '18 at 08:23
  • @FrederikGheysels Pretty sure all the functions in HttpClient have timeouts. Also, this is a webapplication and the only viable is to wait for the results no matter what. So absolutely no need for multi threading. – Espen May 02 '18 at 08:24
  • 1
    `this is a webapplication` just let the async propagate, and get with the 2018's, also use ConfigureAwait(false) where possible – TheGeneral May 02 '18 at 08:26
  • @TheGeneral Explain to me why create and use async functions when you have to call them every singe time with await before moving on, countereffecting async completely? – Espen May 02 '18 at 08:34
  • While 'awaiting' for I/O operations, the thread you're working on can be used for other compute-intensive operations until the I/O operation (posting your request; reading the response) is complete – Frederik Gheysels May 02 '18 at 08:38
  • 1
    Firstly async doesn't mean parallel. Asynchronous execution frees a valuable threadpool thread from blocking for an external resource, for no complexity or performance cost. This means the same IIS machine can handle more concurrent requests, ie its more scalable generally – TheGeneral May 02 '18 at 08:39
  • @TheGeneral I'm in luck then as Task.Run does just that but now without having it bubbling up through all parent functions. – Espen May 02 '18 at 08:44
  • Go async all the way if you can. You get responsiveness that is noticable especially in web applications. – FCin May 02 '18 at 11:12

1 Answers1

2

The short answer is - no, there's no generic way to make a synchronous function out of a a task-based asynchronous one.

The problem is that you don't know how it is implemented internally. Say, the asynchronous function is implemented using async and it's running (partially) in the context of the primary thread. Then, if the caller code is trying to block the primary thread by a blocking call, then the async function is blocked, too, which is causing a deadlock.

But, in your particular case you can try to create a new task, call the async function from that task and take its result. There are good chances that it will work, but no guarantee (as mentioned above).

The code would look like this:

using (var response = Task.Run(() => httpClient.PostAsync(url, body).Result).Result)
{
    ...
}
armenm
  • 922
  • 6
  • 9
  • This will block entire ThreadPool thread. Also from what I know blocking inside a task has undefined behaviour. One more thing, this "response" will actually be a task, because you didn't wait for it, so this will not even compile. – FCin May 02 '18 at 11:09
  • @FCin blocking a thread from thread pool is OK, I'm taking that thread specially for that. Thanks for the second comment - I corrected the code. – armenm May 02 '18 at 11:12
  • It is not fine if you call this method many times. Each time it will schedule another threadpool thread causing it to completely block ThreadPool and making your application unresponsive for a while. – FCin May 02 '18 at 11:13
  • Also calling `.Result` without `ConfigureAwait` will deadlock your asp.net application, because Task will wait for SynchronizationContext (UI) thread to be free, but it will never be free, because it will wait for Task to finish. – FCin May 02 '18 at 11:15
  • @FCin my code will not use more than one thread at a time from the thread pool. I don't get what you mean by saying "block ThreadPool". – armenm May 02 '18 at 11:18
  • When you first call this method, a threadpool thread will be scheduled to execute this method. This will make this thread "busy". Next time you call this method, another thread will be scheduled, because the previous one is still executing the code. If you call this method a lot in a short period of time, you will run out of threads, because each of them will be busy waiting for the task to end. Then ThreadPool will try to add another thread which is very costly and the performance of the application will decrease drastically. So this means, this method cannot be called a lot without breaking. – FCin May 02 '18 at 11:22
  • @FCin "because the previous one is still executing the code" - no, the previous thread is already released at this time. I'm waiting until it's released (.Result). So I can call it as frequently as I want. – armenm May 02 '18 at 11:25