Async

Async and Await

You can define an asynchronous block of code by using the async keyword. Code marked with this keyword will expect to use the await keyword in its body.

public async MyMethod()
{
  var result = await AsynchronousMethod();
}

While a particular part of your code is awaiting the result of some asynchronous method, the thread is NOT blocked, this is an import distinction. Even though the execution of the local code that needs the result of the awaited asynchronous routing might be paused, the rest of the thread can still be used to run other resources, leading to better efficiency.

public async MyMethod()
{
  Console.WriteLine("Start");
  var result = await AsynchronousMethod(); // <= Execution in this method pauses until complete
  Console.WriteLine(result);
  var extension = await AsynchronousAddMethod(result); // <= Execution in this method pauses until complete
  Console.WriteLine(extension);
}

Task

Task is a structure in .NET that is similar to a Promise in functional languages. A Task object wraps the result of asynchronous code that can be awaited, allowing you to trigger multiple Tasks running from an async code block.

Asynchronous Tasks

You can define an asynchronous method by returning a Task<TResult> from the method, with the type set to the thing that the task should resolve to. This resolution takes place when the await keyword is applied to the result of the method that returns a Task. You only need to return a result that matches the TResult type contained in the Task, the conversion between the two is done automatically, however, the method itself needs to use the async keyword for this implicit conversion to run.

public async Task<string> GetMessageAsync()
{
  Task.Delay(3000).Wait();
  return "Hello" // => implicitly converted into a Task<string>
}

Tasks begin running once the method that returns them is called. This means you can trigger an asynchronous task before its result is needed by returning the Task type result of the asynchronous method and then calling await on it to resolve that task. This can result in faster run times in the local method because the asynchronous requests made for resources at the top of a method can be ready by the time method reaches the await clauses for the resource.

public async MyMethod()
{
  // start the task running
  Task<string> messageTask = GetMessageAsync();
  // ... do other stuff
  // get the value of the result of the task
  string message = await messageTask;
}

You can create a task that returns something using the Factory property on a typed task with the StartNew method with a lambda expression. This task can then be resolved as normal.

var myTask = Task<int>.Factory.StartNew(() => 1001);
var result = await myTask; // => 1001

Synchronous Results

You can get the result of a task synchronously by calling the GetAwait() and GetResult() methods.

var result = myTask.GetAwaiter().GetResult();

When

You can await the completion of multiple tasks simultaneously by using the WhenAll method. This method accepts an arbitrary number of tasks as its arguments and returns another Task that resolves when all the supplied tasks complete.

Task<string> task1 = GetMessageAsync();
Task<string> task2 = GetMessageAsync();
Task<string> task3 = GetMessageAsync();

await Task.WhenAll(task1, task2, task3);

You can get the combined results of a WhenAll call as a collection by assigning the result of awaiting WhenAll to a variable. This only works if the tasks passed in ALL resolve to the same type.

Task<string> task1 = GetMessageAsync();
Task<string> task2 = GetMessageAsync();
Task<string> task3 = GetMessageAsync();

string[] results = await Task.WhenAll(task1, task2, task3);
// can also be written as: var results = await Task.WhenAll(task1, task2, task3);

for(var message in results) {
  // iterate through each message
}

You can pass a collection of Tasks to the WhenAll method instead of a splat list of Tasks. This can be a List or any array or other collection type.

Task<string[] tasks = new Task<string>[] {
  GetMessageAsync(),
  GetMessageAsync(),
  GetMessageAsync()
}

string[] results = await Task.WhenAll(tasks);

for(var message in results) {
  // iterate through each message
}

Delay

You can make a thread wait for a set period of time using the Task object with the Delay and Wait methods. Delay takes the wait time in milliseconds.

// make the thread wait for 3 seconds
Task.Delay(3000).Wait();