WhenAny and Pattern Matching with Different Types of Task

I recently hit a scenario where I wanted to query two different datastores at the same time, but all I wanted was the first answer - it didn’t matter to me which returned first. In a later post, I will show how to do that, but for now I’m keeping this example simple.

I have two async methods, one that returns a string and one that returns an int, both with a random delay built in to simulate calling out to a data store.

public static async Task<int> GetIntAsync()
{
    await Task.Delay(random.Next(100, 500));
    return 123;
}

public static async Task<string> GetStringAsync()
{
    await Task.Delay(random.Next(100, 500));
    return "123456789";
}

I call GetIntAsync and GetStringAsync asynchronously and await the two tasks -

Task<int> getIntTask = GetIntAsync();
Task<string> getStringTask = GetStringAsync();

var result = await Task.WhenAny(getIntTask, getStringTask);

Then with a bit of pattern matching (thanks Andrew Lock for the suggestion), I can tell which one finished and extract the value I need.

if (result is Task<int> intTask)
{
    int value = await intTask;
    Console.WriteLine($"GetIntAsync completed first: {value}");
}
else if (result is Task<string> stringTask)
{
    string value = await stringTask;
    Console.WriteLine($"GetStringAsync completed first: {value}");
}

For ease of reading, here is the full source code -

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Threading.Tasks;
                    
public class Program
{
    public static async Task Main()
    {
        Task<int> getIntTask = GetIntAsync();
        Task<string> getStringTask = GetStringAsync();

        var result = await Task.WhenAny(getIntTask, getStringTask);

        if (result is Task<int> intTask)
        {
            int value = await intTask;
            Console.WriteLine($"GetIntAsync completed first: {value}");
        }
        else if (result is Task<string> stringTask)
        {
            string value = await stringTask;
            Console.WriteLine($"GetStringAsync completed first: {value}");
        }
    }
    
    public static async Task<int> GetIntAsync()
    {
        await Task.Delay(random.Next(100, 500));
        return 123;
    }
    
    public static async Task<string> GetStringAsync()
    {
        await Task.Delay(random.Next(100, 500));
        return "123456789";
    }

    static Random random = new Random();
}
comments powered by Disqus

Related