C#AsyncIntermediate

Task / Task<T>

`Task` represents an asynchronous operation that does not return a value, while `Task<T>` represents an asynchronous operation that returns a value of type `T`.

Review the syntaxStudy the examplesOpen the coding app
Task myTask = SomeAsyncOperation();

This static page keeps the syntax and examples indexed for search, while the coding app handles interactive exploration and saved references.

What it does

Overview

`Task` represents an asynchronous operation that does not return a value, while `Task<T>` represents an asynchronous operation that returns a value of type `T`.

`Task` and `Task<TResult>` are fundamental types in C#'s Task Parallel Library (TPL) and the foundation for `async`/`await`. A `Task` object represents a unit of work that can be executed asynchronously, potentially on a separate thread or by leveraging I/O completion ports. It encapsulates the state of an asynchronous operation, including whether it's running, completed successfully, canceled, or faulted. `Task<TResult>` extends `Task` by adding a generic type parameter `TResult`, indicating that the asynchronous operation will eventually produce a result of that type. When an `async` method is called, it immediately returns a `Task` or `Task<TResult>` instance, allowing the caller to continue execution. The actual work of the `async` method proceeds in the background. You can `await` these `Task` objects to pause execution until the operation completes, or use methods like `ContinueWith` for continuation-based programming. Time complexity is not directly applicable to `Task` objects themselves, but rather to the operations they represent. Edge cases include observing exceptions (unhandled exceptions in `Task`s can be problematic if not awaited or handled via `Exception` property), and managing task lifetimes. Best practices involve always `await`ing tasks or handling their exceptions explicitly to prevent silent failures, and understanding the difference between `Task.Wait()` (blocking) and `await` (non-blocking).

Quick reference

Syntax

Task myTask = SomeAsyncOperation();

See it in practice

Examples

1

Creating and awaiting a non-generic Task

using System.Threading.Tasks;
using System;

public class TaskExample
{
    public static async Task DoSomethingAsync()
    {
        Console.WriteLine("Task started.");
        await Task.Delay(1000);
        Console.WriteLine("Task finished.");
    }

    public static async Task Main()
    {
        Console.WriteLine("Main before task.");
        await DoSomethingAsync();
        Console.WriteLine("Main after task.");
    }
}
Output:
Main before task. Task started. Task finished. Main after task.

Illustrates a `Task` representing an asynchronous operation that doesn't return a value, and how `await` pauses the caller until it completes.

2

Creating and awaiting a Task<T> with a result

using System.Threading.Tasks;
using System;

public class TaskTExample
{
    public static async Task<string> FetchDataAsync()
    {
        Console.WriteLine("Fetching data...");
        await Task.Delay(1500);
        return "Data from server";
    }

    public static async Task Main()
    {
        Console.WriteLine("Main requesting data.");
        string data = await FetchDataAsync();
        Console.WriteLine($"Received: {data}");
    }
}
Output:
Main requesting data. Fetching data... Received: Data from server

Shows `Task<string>` for an operation that returns a string, and how `await` retrieves the result.

3

Handling exceptions from a Task

using System.Threading.Tasks;
using System;

public class TaskExceptionExample
{
    public static Task SimulateErrorAsync()
    {
        return Task.Run(() => { throw new Exception("Error in background task!"); });
    }

    public static async Task Main()
    {
        try
        {
            await SimulateErrorAsync();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Caught: {ex.Message}");
        }
    }
}
Output:
Caught: Error in background task!

Demonstrates that exceptions thrown within a `Task` are propagated when the `Task` is awaited, allowing for standard `try-catch` handling.

Debug faster

Common Errors

1

Unobserved Task Exception

Cause: An exception occurs in a `Task` that is not awaited and its exception is never retrieved (e.g., by accessing `.Exception` property). In .NET Core and .NET 5+, unobserved task exceptions no longer crash the process by default.

Fix: Always `await` tasks or explicitly handle their exceptions (e.g., by accessing `task.Exception` and marking it as observed) to prevent silent failures or unexpected behavior.

public Task DoWorkAndFail() { return Task.Run(() => { throw new Exception("Oops!"); }); }
// In Main, if you just call:
// DoWorkAndFail(); // The exception might go unobserved

Runtime support

Compatibility

.NETN/A.NET Framework 4.0+, .NET Core 1.0+

Source: Microsoft Learn

Common questions

Frequently Asked Questions

`Task` represents an asynchronous operation that does not return a value, while `Task<T>` represents an asynchronous operation that returns a value of type `T`.

Unobserved Task Exception: Always `await` tasks or explicitly handle their exceptions (e.g., by accessing `task.Exception` and marking it as observed) to prevent silent failures or unexpected behavior.