TaskCompletionSource in .NET to Convert Async Non Task to Async Task

What is TaskCompletionSource<T>?

TaskCompletionSource<T> is a class in the .NET framework that allows you to manually create and control the lifecycle of a Task<T>. It provides a way to complete tasks programmatically, which can be particularly useful when you need to adapt non-task-based asynchronous code or create custom asynchronous patterns.

Key Features and Methods

  1. Creation: You instantiate a TaskCompletionSource<T> with a specific result type T, which indicates the type of the result that the Task<T> will eventually produce.
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    
  2. Setting Result: Use the SetResult method to set the result of the task when your asynchronous operation completes successfully.
    tcs.SetResult(42);
    
  3. Setting Exception: If an error occurs during your asynchronous operation, you can set an exception using SetException.
    tcs.SetException(new Exception("Operation failed."));
    
  4. Setting Cancellation: Optionally, you can also cancel the task using SetCanceled.
    tcs.SetCanceled();
    
  5. Accessing the Task: Obtain the Task<T> instance using the Task property of TaskCompletionSource<T>.
    Task<int> task = tcs.Task;
    
  6. Asynchronous Operations: TaskCompletionSource<T> is particularly useful when you need to await an external event or handle scenarios where direct use of async methods isn't suitable.

Example Usage

To illustrate the usage of TaskCompletionSource<T>, let's consider a simplified example where we simulate an asynchronous operation with a delay.

public async Task<int> DelayedOperationAsync()
{
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    // Simulate a delayed operation
    await Task.Delay(2000); // Wait for 2 seconds
    // Set the result of the task
    tcs.SetResult(42);
    return await tcs.Task; // Await the completion of the task
}

In this example.

  • We create an instance of TaskCompletionSource<int>.
  • We simulate a delay using Task. Delay.
  • After the delay, we set the result of the task to 42 using SetResult.
  • We then await the completion of the task and return its result.

Use Cases

  1. Adapting Non-Task-Based APIs: TaskCompletionSource<T> is ideal for integrating with APIs that are not inherently based on tasks or async methods. For example, you might have an event-based API where you need to await the completion of an event handler.
  2. Custom Asynchronous Patterns: In scenarios where the standard async-await pattern is not flexible enough, TaskCompletionSource<T> allows you to implement custom asynchronous logic. This can be particularly useful in complex systems or libraries.
  3. Event-Based Asynchronous Operations: Convert traditional event-based asynchronous operations into tasks that can be awaited. This can simplify the code and make it more readable and maintainable.

Advanced Example Event-Based asynchronous operation

Consider a scenario where you need to convert an event-based API into a task-based one.

public Task<string> GetDataAsync()
{
    TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
    // Simulate an event-based operation
    EventHandler handler = null;
    handler = (sender, args) =>
    {
        // Unsubscribe from the event
        SomeEvent -= handler;

        // Set the result of the task
        tcs.SetResult("Data received");
    };
    // Subscribe to the event
    SomeEvent += handler;
    // Start the event-based operation
    StartEventBasedOperation();
    return tcs.Task;
}

In this example.

  • We create a TaskCompletionSource<string> to represent the asynchronous operation.
  • We subscribe to an event and set the result of the task when the event is triggered.
  • We return the task to the caller, allowing them to await its completion.

Conclusion

TaskCompletionSource<T> is a versatile and powerful tool in the .NET framework for creating and controlling tasks manually. It bridges the gap between traditional asynchronous patterns and the modern task-based asynchronous programming model. By understanding and utilizing TaskCompletionSource<T>, you can handle complex asynchronous scenarios more effectively, making your code more robust, readable, and maintainable.


Similar Articles