Function button_click is declared async. This function calls an async function. Calling an async function does not run the code immediately as calling a normal function would do. Instead the code is scheduled as a request to run a Task by one of the threads in a collection of available threads that is known as the thread pool.
After scheduling the task, the caller of the async function continues with the next statements until it reaches the statement to await for the result of the scheduled task. Quite often, the await is immediately after the call to the async function, but it does not have to.
Task<int> longRunningTask = LongTaskAsync();
// because of not awaiting, your thread is free to do other things:
DoSomethingElse();
// now I need the result: await for the longRunningTask
int i = await longRunningTask;
ProcessResult(i);
Whenever one of the threads in the thread pool is free, it will check if a piece of code is scheduled and if so it will run the code. If this thread calls another async function, the task is scheduled etc.
Most async functions will somewhere await for the results of the tasks they scheduled. In fact, your compiler will warn you if you forget to await the task. To enable your callers to await for your async function to complete, your function returns Task
or Task<TResult>
instead of void
or TResult
.
The only exception is the event handler: this method can return void
. This has the effect that the task will be scheduled, but that the caller cannot await for it.
This caller is your application. The application schedules a task to handle the button clicked event but does not await until this event is finished. Hence your application will remain responsive.
Because button_click
is async, your UI thread does not wait for this function to finish. Hence your program could enter this function again before LongTaskAsync
is finished. Quite often this is an undesired effect and you should take care that this does not happen, for instance by disabling the button at the entrance of the event function and enable it when the event is finished. Something like this:
private async void Button_Click(object sender, RoutedEventArgs e)
{
// Prevent entering again before this function is finished:
Button clickedButton = (Button)sender;
clickedButton.Enabled = false;
// process the clicked button:
Task<int> i = LongTaskAsync(); // first break point here
int k = await i;
print("Done, i=" + i);
// Finished processing, enable the button again:
clickedButton.Enabled = true;
}