Sitemap
Cem Dırman

Cem Dırman

Software Engineer @Atmosware || Java Lover || Traveller

Member-only story

C# & .NET for Java/Spring Developers-2

6 min readOct 27, 2026
Press enter or click to view image in full size

In Part 1 we compared project files and dotnet CLI basics. This time it’s async. If you’ve written a Spring Boot service that fans out to two or three downstream calls, you’ve reached for CompletableFuture, WebClient’s reactive chain, or, since Java 21, a virtual thread that lets you write it like blocking code again and let the runtime worry about the rest. C# solved this at the language level a decade earlier with async/await, and Task<T> ends up looking a lot like CompletableFuture<T> with a friendlier syntax on top.

This part covers the mechanics: how Task<T> compares to CompletableFuture<T>, how await replaces .thenApply/.thenCompose chains, how errors propagate, how you actually run things in parallel instead of accidentally serializing them, and where cancellation differs between the two runtimes.

The Mental Model: Task<T> vs CompletableFuture<T>

Both represent a value that will exist eventually. The difference is how much the language helps you work with that value.

public class InventoryClient
{
private readonly HttpClient _httpClient;

public InventoryClient(HttpClient httpClient)
{
_httpClient = httpClient;
}

public async Task<InventoryLevel> GetInventoryAsync(string sku, CancellationToken cancellationToken = default)
{…

--

--

Cem Dırman
Cem Dırman

Written by Cem Dırman

Software Engineer @Atmosware || Java Lover || Traveller