How async, await and Task are related in C#?

Introduction

The following one-line summary explains how async, await, and Task work together:

  • Task: Represents an asynchronous operation.

  • async: Enables a method to use the await keyword.

  • await: Pauses the execution of the method until the task completes, without blocking a thread.

async and await are used to perform non-blocking asynchronous operations. Instead of blocking a thread while waiting for an I/O operation (such as a SQL call, HTTP call, or file operation), the thread is returned to the Thread Pool and can process other work.

It improves application scalability and makes efficient use of threads. In UI applications, it also improves responsiveness.

What Does async Do?

async tells the compiler that this method contains asynchronous operations. The compiler generates a state machine so the method can pause and later resume after each await.

What Does await Do?

await tells the compiler to asynchronously wait for an I/O operation (such as SQL, HTTP, or file I/O). While waiting, the current ThreadPool worker thread is returned to the ThreadPool so it can process other work.

Example

Let's understand it with the following code example:

public async Task<List<Product>> GetAllProductsAsync()
{
    var products = await repository.GetAllProductFromSQL();
    return products;
}

Code Explanation

Line 1

Since it is an async method, let's assume ThreadPool worker Thread #18 (thread number used only for illustration) starts executing it.

Line 2

Thread #18 starts the SQL request. Since the operation is asynchronous, the method pauses, Thread #18 returns to the ThreadPool, and the compiler-generated state machine remembers where execution should resume, which has information like:

  • Current Method: GetAllProductsAsync

  • Current local variable: products

  • Resume execution after:

await repository.GetAllProductFromSQL();

What Happens When the SQL Operation Completes?

When the SQL operation completes, the .NET runtime marks the Task as completed and schedules the continuation on any available ThreadPool worker thread.

Why Task<List<Product>> Instead of List<Product>?

The products are not available immediately. The SQL operation is still running, so the method immediately returns a Task<List<Product>>, which represents work in progress.

Once the SQL operation completes, that Task contains the final result.

What Information Does a Task Store?

A Task keeps information such as:

PropertyDescription
StatusRunning, Completed, Faulted, Cancelled
ResultAvailable after successful completion
ExceptionContains any exception thrown during execution

Summary

Task represents an asynchronous operation, async enables a method to perform asynchronous work using await, and await allows the method to pause without blocking a thread while waiting for an operation to complete. During this time, the ThreadPool worker thread is returned to the ThreadPool to process other requests, improving scalability, resource utilization, and application responsiveness. Once the asynchronous operation completes, execution resumes from the point after the await, and the Task eventually contains either the result or an exception if the operation fails