Skip to content

Cos'è RxJS?

Panoramica

RxJS (Reactive Extensions for JavaScript) è una libreria per la "programmazione reattiva" in JavaScript.

Cos'è la programmazione reattiva?

La programmazione reattiva è un metodo per creare programmi che si aggiornano automaticamente in risposta alle variazioni dei dati. È un tipo di programmazione guidata dagli eventi, con particolare attenzione alla gestione di flussi di dati asincroni. Si concentra sui flussi di dati (stream) e costruisce i programmi in modo da reagire a questi flussi.

In altre parole, RxJS è una libreria per la gestione di eventi e flussi di dati asincroni (stream) in stile funzionale, che utilizza il pattern Observable e fornisce potenti strumenti per la gestione di flussi di dati asincroni.

Observable è un costrutto fondamentale di RxJS per rappresentare eventi e flussi di dati asincroni (stream). Un Observable è un "flusso di dati" che pubblica valori nel tempo. È possibile ricevere il valore sottoscrivendolo (subscribe).

TIP

Se vi state chiedendo "Cos'è uno stream?", consultate anche Cos'è uno stream?.

Esempio semplice di utilizzo

ts
import { fromEvent } from 'rxjs';

fromEvent(document, 'click').subscribe(event => {
  console.log('Cliccato:', event);
});

Componenti base di RxJS

Per padroneggiare RxJS, è importante comprendere i seguenti componenti fondamentali:

ComponentePanoramica
ObservableLa sorgente di stream che rappresenta dati che si verificano in modo asincrono o nel tempo.
Observer[1]L'entità che sottoscrive e riceve dati dall'Observable.
SubscriptionGestisce la sottoscrizione e la cancellazione di un Observable.
Creation FunctionsUn insieme di funzioni per creare e combinare Observable.
OperatorUn insieme di funzioni per trasformare e controllare Observable.
Subject[2]Un relay con proprietà sia di Observable che di Observer.
Scheduler[3]Un meccanismo per controllare i tempi di esecuzione degli Observable.

Questi componenti hanno funzioni indipendenti ma lavorano insieme. Ad esempio, le Creation Functions creano e combinano gli Observable, gli Operator li trasformano e controllano, gli Observer li sottoscrivono e gli Scheduler ne controllano i tempi di esecuzione, costituendo così l'elaborazione del flusso nel suo complesso.

Componenti RxJS e flusso di dati

※ L'uso dettagliato e gli esempi di ciascun componente sono spiegati separatamente nei rispettivi capitoli dedicati.

Diagramma delle classi

Vantaggi di RxJS

VantaggioDescrizione
Codice dichiarativo[4]Descrive "cosa si vuole fare" con map, filter, ecc. evitando descrizioni procedurali come i cicli for
Semplificazione dell'elaborazione asincronaEvita l'annidamento di Promise e callback, permettendo di scrivere in modo intuitivo
Gestione degli erroriGestione uniforme degli errori nei flussi con .pipe(catchError(...)), ecc.
CancellabileI flussi possono essere interrotti con Subscription.unsubscribe()
Operatori diversificatiTrasformazione e composizione con numerosi operatori come debounceTime, mergeMap, combineLatest, ecc.

Casi d'uso

RxJS è utile in tutte le situazioni in cui si gestiscono "dati che cambiano nel tempo". Di seguito sono riportate le principali aree di utilizzo.

Comunicazione in tempo reale e streaming

RxJS è particolarmente potente quando si tratta di comunicazioni in tempo reale come WebSocket e Server-Sent Events (SSE).

UtilizzoDescrizioneOperatori principali
Comunicazione WebSocketChat, notifiche, aggiornamenti azionari, ecc.webSocket, filter, map
Server-Sent EventsNotifiche push dal serverfromEvent, retry
Monitoraggio sensori IoTElaborazione continua di dati dei sensoridebounceTime, distinctUntilChanged

Esempio semplice

ts
import { webSocket } from 'rxjs/webSocket';
import { filter } from 'rxjs';

const socket$ = webSocket('wss://example.com/chat');

socket$.pipe(
  filter(msg => msg.type === 'message')
).subscribe(msg => console.log('Nuovo:', msg.text));

Gestione UI/stato e controllo form

Permette di gestire in modo reattivo gli input dell'utente e i cambiamenti di stato.

Relazione con i framework

I moderni framework front-end (Angular Signals, React hooks, Vue Composition API, Svelte Runes, ecc.) forniscono ciascuno il proprio sistema reattivo. RxJS, come libreria indipendente dal framework, può essere utilizzato insieme a questi o separatamente. L'integrazione di meccanismi specifici dei framework con RxJS è spiegata in dettaglio nel Capitolo 15 "Integrazione con i framework" (in preparazione).

UtilizzoDescrizioneOperatori principali
Controllo form di inputAutocompletamento ricerca, validazione in tempo realedebounceTime, distinctUntilChanged, switchMap
Collegamento di più elementi formAggiornamento di campi di input dipendenticombineLatest, withLatestFrom
Comunicazione tra componentiEvent bus e gestione stato personalizzataSubject, share
Elaborazione eventi UIClick, scroll, drag & dropfromEvent, takeUntil

Esempio semplice

ts
import { fromEvent, combineLatest } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs';

const searchInput = document.querySelector('#search') as HTMLInputElement;
const sortSelect = document.querySelector('#sort') as HTMLInputElement;

const search$ = fromEvent(searchInput, 'input').pipe(
  map(e => (e.target as HTMLInputElement).value)
);

const sort$ = fromEvent(sortSelect, 'change').pipe(
  map(e => (e.target as HTMLSelectElement).value)
);

combineLatest([search$, sort$]).pipe(
  debounceTime(300),
  switchMap(([query, order]) =>
    fetch(`/api/search?q=${query}&sort=${order}`).then(r => r.json())
  )
).subscribe(results => console.log(results));

Supporto offline e PWA

Può essere utilizzato per il supporto offline e la gestione dello stato della rete nelle Progressive Web App (PWA).

UtilizzoDescrizioneOperatori principali
Monitoraggio stato reteRilevamento online/offlinefromEvent, merge
Riprova quando offlineRisincronizzazione automatica al ripristino della connessioneretry, retryWhen
Controllo cacheIntegrazione con Service WorkerswitchMap, catchError

Esempio semplice

ts
import { fromEvent, merge } from 'rxjs';
import { map, startWith } from 'rxjs';

const online$ = fromEvent(window, 'online').pipe(map(() => true));
const offline$ = fromEvent(window, 'offline').pipe(map(() => false));

merge(online$, offline$).pipe(
  startWith(navigator.onLine)
).subscribe(isOnline => {
  console.log(isOnline ? 'Online' : 'Offline');
});

API AI/Streaming

Ideale anche per gestire le risposte di API in streaming come OpenAI.

UtilizzoDescrizioneOperatori principali
Output sequenziale tokenVisualizzazione in tempo reale delle risposte AIconcatMap, scan
Elaborazione streamingElaborazione Server-Sent EventsfromEvent, map
Integrazione backendUtilizzo con NestJS (RxJS integrato)Vari operatori

Comunicazione HTTP e gestione errori

Permette di gestire elegantemente la comunicazione HTTP asincrona.

UtilizzoDescrizioneOperatori principali
Richieste APIComunicazione con API RESTfulswitchMap, mergeMap
Gestione erroriRetry e fallbackcatchError, retry
Controllo timeoutLimiti di tempo per le rispostetimeout
CancellazioneInterruzione di richieste non necessarietakeUntil, unsubscribe()

Gestione stato e architettura

Può essere utilizzato anche per la progettazione dell'architettura generale dell'applicazione.

UtilizzoDescrizioneOperatori principali
Librerie di gestione statoNgRx, Redux-Observable, ecc.scan, share
Gestione flusso eventiUtilizzo in DDD (Domain Driven Design)Subject, shareReplay
Separazione data layerClean ArchitectureVari operatori

TIP

Per informazioni su come scegliere tra Promise e RxJS, vedere anche Differenze tra Promise e RxJS.

RxJS offre un approccio potente alla programmazione asincrona e basata sugli eventi. Il concetto di flusso di dati incentrato sugli Observable è particolarmente utile quando si gestisce un'elaborazione asincrona complessa.


  1. Nell'implementazione viene utilizzata la classe Subscriber. Per maggiori dettagli, vedere Differenza tra Observer e Subscriber. ↩︎

  2. Subject è un'entità speciale che può agire sia come Observable che emette valori, sia come Observer che riceve valori. ↩︎

  3. Scheduler viene utilizzato per controllare i tempi e il contesto dell'esecuzione asincrona, ed è utile anche per il debugging e la gestione delle prestazioni. ↩︎

    • Codice dichiarativo: Codice che descrive direttamente "quale risultato si desidera"
    • Codice procedurale: Codice che descrive "quali calcoli eseguire per ottenere il risultato desiderato"
    ↩︎

Pubblicato sotto licenza CC-BY-4.0.