TS
C#

TypeScript to C#

10 lessons

Progress0%
1Introduction: Two Languages, One Mind2Type Systems: Structural vs Nominal3Classes: Advanced Features4Generics and LINQ5Nullable Reference Types6Async/Await7Decorators to Attributes8Ecosystem9File I/O10Records and Pattern Matching
All Mirror Courses
TS
C#
Async/Await
MirrorLesson 6 of 10
Lesson 6

Async/Await

Async Programming

Introduction

In this lesson, you'll learn about async/await in C#. Coming from TypeScript, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
TS
From TypeScript:

In TypeScript, you're familiar with async programming.

C#
In C#:

C# has its own approach to async programming, which we'll explore step by step.

The C# Way

Let's see how C# handles this concept. Here's a typical example:

C#
C# Example
async Task<User> FetchUser(int id) {
    var res = await httpClient.GetAsync($"/api/users/{id}");
    if (!res.IsSuccessStatusCode) throw new Exception("Not found");
    return await res.Content.ReadFromJsonAsync<User>();
}

async Task<User[]> LoadAll(int[] ids) {
    return await Task.WhenAll(ids.Select(FetchUser));
}

Comparing to TypeScript

Here's how you might have written similar code in TypeScript:

TS
TypeScript (What you know)
async function fetchUser(id: number): Promise<User> {
  const res = await fetch(`/api/users/${id}`);
  if (!res.ok) throw new Error("Not found");
  return res.json() as Promise<User>;
}

async function loadAll(ids: number[]): Promise<User[]> {
  return Promise.all(ids.map(fetchUser));
}
Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

async/await syntax is nearly identical between TypeScript and C#

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

Promise<T> → Task<T> in C#

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

Promise.all() → Task.WhenAll()

Mirror Card
TS
From TypeScript:

You may be used to different syntax or behavior.

C#
In C#:

void async functions return Task (not Promise<void>), allowing awaiting

Step-by-Step Breakdown

1. Task<T> = Promise<T>

C# Task<T> is the exact equivalent of TypeScript's Promise<T>. Async methods return Task<T> instead of Promise<T>. The async/await keywords work identically.

TS
TypeScript
async function getUser(): Promise<User> {
  return await fetch("/user").then(r => r.json());
}
C#
C#
async Task<User> GetUser() {
    var res = await httpClient.GetAsync("/user");
    return await res.Content.ReadFromJsonAsync<User>();
}

2. Task.WhenAll = Promise.all

Task.WhenAll() runs multiple tasks concurrently and awaits all of them, exactly like Promise.all().

TS
TypeScript
const users = await Promise.all([fetchUser(1), fetchUser(2)]);
C#
C#
var users = await Task.WhenAll(FetchUser(1), FetchUser(2));

3. async void — Avoid It

C# allows async void methods (for event handlers) but they cannot be awaited and exceptions are unhandled. Always use async Task instead of async void.

TS
TypeScript
// TypeScript: no void async distinction
async function doWork(): Promise<void> {}
C#
C#
// C#: prefer Task over void
async Task DoWork() {} // awaitable
// async void DoWork() {} // avoid — not awaitable
Common Pitfall
async void exceptions crash the application. Only use it for event handlers where the signature requires void.

4. ConfigureAwait

In library code, add .ConfigureAwait(false) after awaits to avoid context deadlocks. Not needed in ASP.NET Core (no SynchronizationContext) but good practice in libraries.

TS
TypeScript
// TypeScript has no equivalent — runs in single-threaded event loop
C#
C#
// In library code:
var data = await GetDataAsync().ConfigureAwait(false);
// Prevents deadlocks in WinForms/WPF context
Rule of Thumb
Add ConfigureAwait(false) in library code. Omit it in application code (ASP.NET Core, console apps).

Common Mistakes

When coming from TypeScript, developers often make these mistakes:

  • async/await syntax is nearly identical between TypeScript and C#
  • Promise<T> → Task<T> in C#
  • Promise.all() → Task.WhenAll()
Common Pitfall
Don't assume C# works exactly like TypeScript. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • async/await syntax is nearly identical in TypeScript and C# — they share the same design
  • Promise<T> → Task<T>, Promise.all → Task.WhenAll
  • Avoid async void — use async Task instead
  • Use ConfigureAwait(false) in library code to prevent context deadlocks
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your TypeScript code in C# to practice these concepts.
PreviousNext