Async/Await

Edoardo Midali
Edoardo Midali

La programmazione asincrona rappresenta un paradigma fondamentale nello sviluppo moderno, permettendo l’esecuzione non-bloccante di operazioni che richiedono tempo. TypeScript potenzia questo paradigma fornendo tipizzazione statica completa per operazioni asincrone, garantendo type safety anche in contesti dove il valore di ritorno è disponibile solo in futuro.

Concetti Fondamentali

Il pattern async/await in TypeScript si basa sul tipo Promise<T> che rappresenta un valore che sarà disponibile in futuro. Una funzione marcata come async restituisce automaticamente una Promise, anche se il valore di ritorno è sincrono. La keyword await converte codice asincrono in una sintassi che appare sincrona.

async function fetchUserName(id: number): Promise<string> {
  const response = await fetch(`/api/users/${id}`);
  const user = await response.json();
  return user.name; // TypeScript inferisce Promise<string>
}

Tipizzazione delle Promise

TypeScript fornisce controllo di tipo completo per le Promise, specificando il tipo del valore risolto e prevenendo errori comuni.

interface ApiResult<T> {
  success: boolean;
  data: T;
  error?: string;
}

async function processApiResponse(): Promise<ApiResult<User[]>> {
  const response = await fetch("/api/users");
  const users: User[] = await response.json();
  return { success: true, data: users };
}

Gestione degli Errori

TypeScript permette di tipizzare anche gli errori, migliorando la robustezza del codice e facilitando la gestione di diversi tipi di errori.

class ValidationError extends Error {
  constructor(message: string, public field: string) {
    super(message);
  }
}

async function validateUser(data: UserInput): Promise<User> {
  if (!data.email.includes("@")) {
    throw new ValidationError("Invalid email", "email");
  }
  // ... resto della logica
}

Operazioni Parallele

TypeScript supporta operazioni asincrone parallele mantenendo la tipizzazione completa attraverso Promise.all, Promise.race e Promise.allSettled.

async function fetchUserProfile(userId: number): Promise<UserProfile> {
  const [user, posts, followers] = await Promise.all([
    fetchUser(userId), // Promise<User>
    fetchUserPosts(userId), // Promise<Post[]>
    fetchUserFollowers(userId), // Promise<User[]>
  ]);
  return { user, posts, followers };
}

Async Iterators

TypeScript supporta async iterators e generators per processare stream di dati asincroni in modo elegante e type-safe.

async function* fetchUsersBatch(): AsyncGenerator<User[], void, unknown> {
  let page = 1;
  while (true) {
    const users = await fetchUsersPage(page++);
    if (users.length === 0) break;
    yield users;
  }
}

for await (const userBatch of fetchUsersBatch()) {
  // userBatch è tipizzato come User[]
}

Utility Types per Async

TypeScript fornisce utility types specializzati per operazioni asincrone che facilitano la manipolazione di tipi Promise.

type Awaited<T> = T extends Promise<infer U> ? U : T;
type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends (
  ...args: any
) => Promise<infer R>
  ? R
  : never;

// Trasforma tutti i metodi in async
type AsyncMethods<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => any
    ? (...args: Parameters<T[K]>) => Promise<ReturnType<T[K]>>
    : T[K];
};

Pattern Avanzati

La tipizzazione facilita l’implementazione di pattern robusti come retry, timeout, circuit breaker e fallback con type safety mantenuta.