En aquest tema, explorarem patrons avançats per gestionar operacions asíncrones en TypeScript. Aquests patrons són útils per escriure codi més net, eficient i mantenible quan es treballa amb operacions asíncrones complexes.
Contingut
- Patró de Retry (Reintentar)
- Patró de Throttling (Limitació)
- Patró de Debouncing (Desacceleració)
- Patró de Circuit Breaker (Interruptor de Circuit)
- Patró de Queues (Cues)
- Patró de Retry (Reintentar)
El patró de Retry és útil quan una operació asíncrona pot fallar temporalment i volem reintentar-la un nombre determinat de vegades abans de donar-nos per vençuts.
Exemple
function retry<T>(fn: () => Promise<T>, retries: number): Promise<T> { return fn().catch(err => { if (retries > 0) { return retry(fn, retries - 1); } else { throw err; } }); } // Exemple d'ús const fetchData = () => fetch('https://api.example.com/data').then(response => response.json()); retry(fetchData, 3) .then(data => console.log(data)) .catch(err => console.error('Failed after 3 retries:', err));
Explicació
- retry: Funció que accepta una funció asíncrona
fn
i un nombre de reintentsretries
. - catch: Si la promesa falla, es reintenta fins que el nombre de reintents s'esgoti.
- Patró de Throttling (Limitació)
El patró de Throttling limita el nombre de vegades que una funció pot ser executada en un període de temps determinat.
Exemple
function throttle<T>(fn: (...args: any[]) => Promise<T>, limit: number): (...args: any[]) => Promise<T> { let inThrottle: boolean; return function(...args: any[]): Promise<T> { if (!inThrottle) { inThrottle = true; setTimeout(() => inThrottle = false, limit); return fn(...args); } else { return Promise.reject('Throttled'); } }; } // Exemple d'ús const fetchDataThrottled = throttle(fetchData, 2000); fetchDataThrottled() .then(data => console.log(data)) .catch(err => console.error(err));
Explicació
- throttle: Funció que accepta una funció asíncrona
fn
i un límit de tempslimit
. - inThrottle: Variable que controla si la funció està en estat de limitació.
- Patró de Debouncing (Desacceleració)
El patró de Debouncing assegura que una funció només s'executi després d'un període de temps determinat des de l'última vegada que va ser invocada.
Exemple
function debounce<T>(fn: (...args: any[]) => Promise<T>, delay: number): (...args: any[]) => Promise<T> { let timeoutId: NodeJS.Timeout; return function(...args: any[]): Promise<T> { clearTimeout(timeoutId); return new Promise((resolve, reject) => { timeoutId = setTimeout(() => { fn(...args).then(resolve).catch(reject); }, delay); }); }; } // Exemple d'ús const fetchDataDebounced = debounce(fetchData, 2000); fetchDataDebounced() .then(data => console.log(data)) .catch(err => console.error(err));
Explicació
- debounce: Funció que accepta una funció asíncrona
fn
i un retarddelay
. - timeoutId: Variable que emmagatzema l'identificador del temporitzador.
- Patró de Circuit Breaker (Interruptor de Circuit)
El patró de Circuit Breaker evita que una aplicació intenti realitzar operacions que probablement fallaran, basant-se en l'historial d'errors recents.
Exemple
class CircuitBreaker { private failureCount: number = 0; private successCount: number = 0; private state: 'CLOSED' | 'OPEN' | 'HALF-OPEN' = 'CLOSED'; private readonly failureThreshold: number; private readonly successThreshold: number; private readonly timeout: number; constructor(failureThreshold: number, successThreshold: number, timeout: number) { this.failureThreshold = failureThreshold; this.successThreshold = successThreshold; this.timeout = timeout; } async execute<T>(fn: () => Promise<T>): Promise<T> { if (this.state === 'OPEN') { throw new Error('Circuit is open'); } try { const result = await fn(); this.onSuccess(); return result; } catch (err) { this.onFailure(); throw err; } } private onSuccess() { this.successCount++; if (this.state === 'HALF-OPEN' && this.successCount >= this.successThreshold) { this.state = 'CLOSED'; this.successCount = 0; } } private onFailure() { this.failureCount++; if (this.failureCount >= this.failureThreshold) { this.state = 'OPEN'; setTimeout(() => { this.state = 'HALF-OPEN'; this.failureCount = 0; }, this.timeout); } } } // Exemple d'ús const circuitBreaker = new CircuitBreaker(3, 2, 5000); circuitBreaker.execute(fetchData) .then(data => console.log(data)) .catch(err => console.error(err));
Explicació
- CircuitBreaker: Classe que implementa el patró de Circuit Breaker.
- execute: Mètode que executa una funció asíncrona i gestiona l'estat del circuit.
- Patró de Queues (Cues)
El patró de Queues gestiona una cua de tasques asíncrones, assegurant que només un nombre determinat de tasques s'executin simultàniament.
Exemple
class AsyncQueue { private queue: (() => Promise<any>)[] = []; private running: number = 0; private readonly concurrency: number; constructor(concurrency: number) { this.concurrency = concurrency; } enqueue(task: () => Promise<any>) { this.queue.push(task); this.runNext(); } private runNext() { if (this.running >= this.concurrency || this.queue.length === 0) { return; } const task = this.queue.shift(); if (task) { this.running++; task().then(() => { this.running--; this.runNext(); }).catch(() => { this.running--; this.runNext(); }); } } } // Exemple d'ús const queue = new AsyncQueue(2); queue.enqueue(() => fetchData().then(data => console.log(data))); queue.enqueue(() => fetchData().then(data => console.log(data))); queue.enqueue(() => fetchData().then(data => console.log(data)));
Explicació
- AsyncQueue: Classe que implementa una cua de tasques asíncrones amb un límit de concurrència.
- enqueue: Mètode que afegeix una tasca a la cua i inicia l'execució si és possible.
Conclusió
En aquest tema, hem explorat diversos patrons avançats per gestionar operacions asíncrones en TypeScript. Aquests patrons ajuden a escriure codi més robust i mantenible quan es treballa amb operacions asíncrones complexes. Practicar aquests patrons i aplicar-los en projectes reals millorarà significativament la qualitat del teu codi asíncron.
Curs de TypeScript
Mòdul 1: Introducció a TypeScript
- Què és TypeScript?
- Configuració de l'entorn de TypeScript
- Tipus bàsics
- Anotacions de tipus
- Compilació de TypeScript
Mòdul 2: Treballant amb Tipus
Mòdul 3: Tipus Avançats
Mòdul 4: Funcions i Mòduls
- Tipus de Funció
- Paràmetres Opcional i per Defecte
- Paràmetres Rest
- Mòduls i Espais de Noms
- Decoradors
Mòdul 5: Programació Asíncrona
Mòdul 6: Eines i Millors Pràctiques
- Linting i Formatació
- Proves de Codi TypeScript
- TypeScript amb Webpack
- TypeScript amb React
- Millors Pràctiques