Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Do I Call an Async Method from a Non-Async Method? [duplicate]

Tags:

I have the below method:

    public string RetrieveHolidayDatesFromSource() {         var result = this.RetrieveHolidayDatesFromSourceAsync();         /** Do stuff **/         var returnedResult  = this.TransformResults(result.Result); /** Where result gets used **/         return returnedResult;     }       private async Task<string> RetrieveHolidayDatesFromSourceAsync() {         using (var httpClient = new HttpClient()) {             var json = await httpClient.GetStringAsync(SourceURI);             return json;         }     } 

The above does not work and seems to not return any results properly. I am not sure where I am missing a statement to force the await of a result? I want the RetrieveHolidayDatesFromSource() method to return a string.

The below works fine but it is synchronous and I believe it can be improved upon? Note that the below is synchronous in which I would like to change to Asynchronous but am unable to wrap my head around for some reason.

    public string RetrieveHolidayDatesFromSource() {         var result = this.RetrieveHolidayDatesFromSourceAsync();         /** Do Stuff **/          var returnedResult = this.TransformResults(result); /** This is where Result is actually used**/         return returnedResult;     }       private string RetrieveHolidayDatesFromSourceAsync() {         using (var httpClient = new HttpClient()) {             var json = httpClient.GetStringAsync(SourceURI);             return json.Result;         }     } 

Am I missing something?

Note: For some reason, when I breakpoint the above Async Method, when it gets to the line var json = await httpClient.GetStringAsync(SourceURI) it just goes out of breakpoint and I can't go back into the method.

like image 444
Samuel Tambunan Avatar asked Apr 01 '16 05:04

Samuel Tambunan


People also ask

Can we call asynchronous methods from another asynchronous method?

There's no rule that says that "asynchronous cannot call asynchronous". There are specific rules in place, such as "future cannot call future". A Queueable can call another Queueable, a Batchable can call another Batchable in the finish method, and Scheduleable methods can call Batchable and Queueable methods.

What happens if we execute an asynchronous method but do not await it?

The call to the async method starts an asynchronous task. However, because no Await operator is applied, the program continues without waiting for the task to complete. In most cases, that behavior isn't expected.

How do you call async method?

The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.

Can you call an async method without await?

You can call this method with or without the await keyword. The syntax with the await keyword looks like this: Customer cust = await GetCustomerById("A123");


Video Answer


1 Answers

Am I missing something?

Yes. Asynchronous code - by its nature - implies that the current thread is not used while the operation is in progress. Synchronous code - by its nature - implies that the current thread is blocked while the operation is in progress. This is why calling asynchronous code from synchronous code literally doesn't even make sense. In fact, as I describe on my blog, a naive approach (using Result/Wait) can easily result in deadlocks.

The first thing to consider is: should my API be synchronous or asynchronous? If it deals with I/O (as in this example), it should be asynchronous. So, this would be a more appropriate design:

public async Task<string> RetrieveHolidayDatesFromSourceAsync() {     var result = await this.DoRetrieveHolidayDatesFromSourceAsync();     /** Do stuff **/     var returnedResult  = this.TransformResults(result); /** Where result gets used **/     return returnedResult; } 

As I describe in my async best practices article, you should go "async all the way". If you don't, you won't get any benefit out of async anyway, so why bother?

But let's say that you're interested in eventually going async, but right now you can't change everything, you just want to change part of your app. That's a pretty common situation.

In that case, the proper approach is to expose both synchronous and asynchronous APIs. Eventually, after all the other code is upgraded, the synchronous APIs can be removed. I explore a variety of options for this kind of scenario in my article on brownfield async development; my personal favorite is the "bool parameter hack", which would look like this:

public string RetrieveHolidayDatesFromSource() {   return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult(); }  public Task<string> RetrieveHolidayDatesFromSourceAsync() {   return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false); }  private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {   var result = await this.GetHolidayDatesAsync(sync);   /** Do stuff **/   var returnedResult  = this.TransformResults(result);   return returnedResult; }  private async Task<string> GetHolidayDatesAsync(bool sync) {   using (var client = new WebClient()) {     return sync         ? client.DownloadString(SourceURI)         : await client.DownloadStringTaskAsync(SourceURI);   } } 

This approach avoids code duplication and also avoids any deadlock or reentrancy problems common with other "sync-over-async" antipattern solutions.

Note that I would still treat the resulting code as an "intermediate step" on the path to a properly-asynchronous API. In particular, the inner code had to fall back on WebClient (which supports both sync and async) instead of the preferred HttpClient (which only supports async). Once all the calling code is changed to use RetrieveHolidayDatesFromSourceAsync and not RetrieveHolidayDatesFromSource, then I'd revisit this and remove all the tech debt, changing it to use HttpClient and be async-only.

like image 155
Stephen Cleary Avatar answered Oct 01 '22 12:10

Stephen Cleary