Async .NET · Lesson 12

Async composition with results

Keep validation synchronous and waiting asynchronous.

How it works

A useful operation separates immediate validation from work that actually waits. LoadTotalAsync validates its count before awaiting the prepared loader, then computes a total from the returned values. The caller awaits the whole operation and controls how to display the result. No fire-and-forget task escapes its lifetime, so a failure or cancellation can travel back to the owner. For larger operations, pass a caller token through every cancellable dependency and return a clearly defined result type for expected domain failures.

Keep reusable calculations free of console writes so they can serve both a command-line display and an application interface.

Test a count of zero and then a negative count. Identify the successful empty result and the rejected argument, then add cancellation without changing who owns the operation.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

Console.WriteLine(await LoadTotalAsync(3));
async Task<int> LoadTotalAsync(int count)
{
    if (count < 0) throw new ArgumentOutOfRangeException(nameof(count));
    var values = await LoadAsync(count);
    return values.Sum();
}
async Task<int[]> LoadAsync(int count)
{
    await Task.Delay(1);
    return Enumerable.Range(1, count).ToArray();
}

Expected output

6
Try this
One awaited result should cover the full lifetime of the operation.