derek
derek

Reputation: 10227

Callback with async and await

I have questions regarding the execution order of async jobs.

I will ask my question with example because it is easier to be understandable.

It is an official example from https://msdn.microsoft.com/en-us/library/mt674882.aspx with some twist.

async Task<int> AccessTheWebAsync()
{ 
    HttpClient client = new HttpClient();

    //async operation:
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoWork1();

    // The await operator suspends AccessTheWebAsync.
    string urlContents = await getStringTask;

    DoWork2();

    return urlContents.Length;
}

Can I say DoWork2 is a callback of client.GetStringAsync?

If so, DoWork2 is not immediately executed following the completion of client.GetStringAsync IF DoWork1 runs longer time than client.GetStringAsync.

Am I right here?

Upvotes: 5

Views: 6316

Answers (2)

Stephen Cleary
Stephen Cleary

Reputation: 456887

Yuval's answer is correct.

To answer your followup question:

Is there a way to make the DoWork2 immediately executed once client.GetStringAsync is finished?

Possibly. It depends on the context of this code. It's not possible to have DoWork1 and DoWork2 both running on the UI thread at the same time (obviously, a single thread can only do one thing at a time). But if this is called from a thread pool context, then sure, it's possible to run them both simultaneously.

However, you should shift your thinking from "callback" to "operation". That is, don't think of it as DoWork2 being a "callback" of GetStringAsync. Instead, think of it like this: I have an operation GetStringAsync, and I have an operation DoWork2; I need a new operation that does one and then the other.

Once you shift your thinking this way, the solution is to write a method for the new operation:

async Task<string> GetStringAndDoWork2Async()
{
  HttpClient client = new HttpClient();
  var result = await client.GetStringAsync("http://msdn.microsoft.com");
  DoWork2();
  return result;
}

Now you can use that new operation in your higher-level method:

async Task<int> AccessTheWebAsync()
{
  var task = GetStringAndDoWork2Async();

  DoWork1();

  string urlContents = await task;

  return urlContents.Length;
}

Upvotes: 6

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149558

DoWork2 is not immediately executed following the completion of client.GetStringAsync if DoWork1 runs longer time than client.GetStringAsync

Once you hit that point await client.GetStrinkAsync, DoWork1() has already completed, as from your example it looks as it's execution is synchronous. Once client.GetStringAsync completes, then DoWork2 is set to execute.

The execution flow will be:

  1. GetStringAsync asynchronously starts. It is executed until it hits it's first internal await, and then yields control back to AccessTheWebAsync.
  2. DoWork1() kicks off. As it's synchronous, everyone waits for it's completion
  3. client.GetStringAsync is asynchronously waited for completion. await will naturally yield the control of execution to it's caller.
  4. Once client.GetStringAsync completes, execution will return to AccessTheWebAsync and DoWork2() will execute synchronously.
  5. urlContents.length will be returned.

Generally, everything after the first await keyword is set to be the continuation for the rest of the method.

Upvotes: 6

Related Questions