C#
async-await
dictionary
task
code-conversion

How to convert Dictionarystring, Taskint to TaskDictionarystring, int

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you have a Dictionary<string, Task<int>>, you already have the keys and you already have asynchronous computations for the values. What you need is a single task that waits for all those value tasks and then rebuilds a dictionary of completed results. The standard tool for that is Task.WhenAll.

The Shape of the Problem

The input type means:

  • each key is already known
  • each value task will eventually produce an int
  • the tasks may already be running

The output type, Task<Dictionary<string, int>>, means one asynchronous operation that completes only when all per-key tasks have completed.

Use Task.WhenAll Over Projected Key-Value Tasks

A clean solution is to project each dictionary entry into a task that returns a completed key-value pair, then await all of them together.

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Threading.Tasks;
5
6public static class Demo
7{
8    public static async Task<Dictionary<string, int>> AwaitDictionaryAsync(
9        Dictionary<string, Task<int>> input)
10    {
11        var pairs = await Task.WhenAll(
12            input.Select(async kvp => new KeyValuePair<string, int>(kvp.Key, await kvp.Value)));
13
14        return pairs.ToDictionary(pair => pair.Key, pair => pair.Value);
15    }
16
17    public static async Task Main()
18    {
19        var input = new Dictionary<string, Task<int>>
20        {
21            ["a"] = Task.FromResult(10),
22            ["b"] = Task.FromResult(20),
23            ["c"] = Task.FromResult(30),
24        };
25
26        var result = await AwaitDictionaryAsync(input);
27        Console.WriteLine(result["b"]);
28    }
29}

This preserves the original keys and awaits all value tasks concurrently.

Why Task.WhenAll Is the Right Tool

Task.WhenAll does not run the tasks by itself. It creates a new task that completes when all supplied tasks complete. That distinction matters.

If the dictionary's Task<int> values are already in progress, WhenAll simply coordinates them. If they have not started yet because they came from lazy factories, then the task creation logic has to happen earlier.

Avoid Awaiting Sequentially in a Loop

This is a common but weaker pattern.

csharp
1var result = new Dictionary<string, int>();
2foreach (var kvp in input)
3{
4    result[kvp.Key] = await kvp.Value;
5}

It works, but it awaits entry by entry. If the tasks are not already all in progress, this can serialize the work. Even when they are already running, it expresses the intent less clearly than Task.WhenAll.

Handle Exceptions Deliberately

If one or more inner tasks fail, the returned task from Task.WhenAll also fails. That is usually what you want because the aggregate result is incomplete.

csharp
1try
2{
3    var result = await AwaitDictionaryAsync(input);
4}
5catch (Exception ex)
6{
7    Console.WriteLine(ex.Message);
8}

If partial success matters, you need a different design, such as wrapping each task result in a success-or-error object rather than allowing exceptions to abort the whole aggregate.

Cancellation Works the Same Way

If one of the inner tasks is canceled, the aggregate task reflects cancellation behavior as well. The exact result depends on whether other tasks faulted or completed. The important point is that Task.WhenAll preserves the semantics of the underlying asynchronous operations rather than hiding them.

Keep the Input Immutable During Aggregation

Do not mutate the dictionary while you are projecting and awaiting its tasks. If the collection changes during enumeration, you can get runtime errors or inconsistent results.

A simple rule is to treat the input dictionary as fixed for the duration of aggregation.

Common Pitfalls

  • Awaiting each dictionary task sequentially instead of aggregating them.
  • Assuming Task.WhenAll starts tasks that have not actually been created yet.
  • Forgetting that one faulted inner task faults the aggregate operation.
  • Mutating the dictionary while enumerating it.
  • Rebuilding the result with logic that accidentally loses keys or duplicates them.

Summary

  • Convert the dictionary by awaiting all value tasks and then rebuilding the completed key-value pairs.
  • 'Task.WhenAll is the standard way to aggregate the asynchronous work.'
  • Project each entry into a task that returns a completed KeyValuePair<string, int>.
  • Be explicit about exception and cancellation behavior.
  • Treat the input dictionary as stable while the aggregation is running.

Course illustration
Course illustration

All Rights Reserved.