Combiner Observables : combineLatest, forkJoin

🏷️ Front-end 📅 07/04/2026 10:00:00 👤 Mezgani Said
Rxjs Observables Combine Angular
Combiner Observables : combineLatest, forkJoin

Combinez plusieurs Observables avec combineLatest, forkJoin, merge, concat. Quand les utiliser, différences de comportement et cas d'usage réels.

Introduction

En Angular et RxJS, vous devez souvent combiner plusieurs Observables : filtres qui changent, chargements parallèles, événements multiples. RxJS offre plusieurs stratégies puissantes selon votre besoin.

À retenir : Chaque operator a une sémantique différente. Choisir le mauvais peut causer des bugs subtils. Comprenez quand émettre et si vous attendez la fin.

combineLatest() — Émettre à chaque changement

combineLatest() combine les valeurs les plus récentes de chaque Observable. Il émet chaque fois que n'importe quel Observable émet une nouvelle valeur :

// Exemple : Filtrer les produits avec 3 critères indépendants
import { combineLatest } from 'rxjs';

@Component({...})
export class ProductListComponent {
    products: Product[] = [];

    constructor(private filterService: FilterService, private api: ApiService) {
        combineLatest([
            this.filterService.category$,  // Catégorie sélectionnée
            this.filterService.search$,     // Texte de recherche
            this.filterService.page$        // Page actuelle
        ]).subscribe(([category, search, page]) => {
            // Appelé chaque fois qu'UN des trois Observables change
            this.loadProducts(category, search, page);
        });
    }

    private loadProducts(category: string, search: string, page: number) {
        this.api.getProducts(category, search, page)
            .subscribe(products => this.products = products);
    }
}
Caractéristiques :
  • ✅ Émet chaque fois qu'UN Observable change (après que tous ont émis au moins une fois)
  • ✅ Parfait pour les filtres en temps réel
  • ❌ N'émet PAS si un Observable n'a pas encore émis de valeur
  • ❌ Ne se termine que quand TOUS les Observables se terminent

forkJoin() — Attendre la fin de tous

forkJoin() attend que TOUS les Observables se terminent, puis émet une seule fois avec les dernières valeurs :

// Exemple : Charger tous les données au démarrage d'une page
import { forkJoin } from 'rxjs';

@Component({...})
export class DashboardComponent implements OnInit {
    users: User[] = [];
    posts: Post[] = [];
    comments: Comment[] = [];
    isLoading = true;

    constructor(private api: ApiService) {}

    ngOnInit() {
        forkJoin({
            users: this.api.getUsers(),
            posts: this.api.getPosts(),
            comments: this.api.getComments()
        }).subscribe({
            next: ({ users, posts, comments }) => {
                // Tous les appels sont terminés
                this.users = users;
                this.posts = posts;
                this.comments = comments;
                this.isLoading = false;
            },
            error: (err) => {
                console.error('Une requête a échoué :', err);
                this.isLoading = false;
            }
        });
    }
}
Avantages :
  • ✅ Parfait pour le chargement initial parallèle
  • ✅ Émet une seule fois avec TOUTES les données
  • ✅ Gère les erreurs facilement
  • ❌ Si UNE requête échoue, tout échoue (pas de fallback)

merge() — Fusionner les émissions

merge() combine les émissions de plusieurs Observables sans attendre. Chaque émission passe au travers :

// Exemple : Déclencher un refresh depuis plusieurs sources
import { merge } from 'rxjs';
import { switchMap, startWith } from 'rxjs/operators';

@Component({...})
export class DataComponent {
    data: any;

    constructor(private api: ApiService, private route: ActivatedRoute) {
        // Refresh au démarrage, quand le bouton est cliqué, ou quand les params changent
        merge(
            of(null),                                    // Émettre immédiatement
            this.refreshButton.click$,                  // Clic sur le bouton refresh
            this.route.params.pipe(skip(1)),           // Route change (skip le premier)
            this.autoRefresh$.pipe(throttleTime(5000)) // Auto-refresh tous les 5s
        ).pipe(
            switchMap(() => {
                console.log('Refresh déclenché');
                return this.api.getData();
            })
        ).subscribe(data => {
            this.data = data;
        });
    }
}
Quand utiliser merge() : Plusieurs sources indépendantes doivent déclencher la même action. C'est un OR logique : "Si A émet OU B émet OU C émet, fais ça".

Tableau comparatif

Operator Quand émettre Attendez tous ? Cas d'usage
combineLatest À chaque changement (après que tous ont émis) Non (premier en retard) Filtres en temps réel, dépendances multiples
forkJoin Une fois à la fin Oui Chargement initial parallèle
merge À chaque émission (n'importe quel Observable) Non Plusieurs sources → une action
zip Une fois, en paires ordonnées Oui (par paires) Synchroniser deux streams parallèles

Conclusion

Combiner les Observables est un art. Demandez-vous :

  • Dois-je attendre la fin de tous ?forkJoin()
  • Dois-je réagir à chaque changement ?combineLatest()
  • Plusieurs sources déclenchent une action ?merge()
  • Dois-je synchroniser en paires ?zip()
Pro tip : En doute ? combineLatest() est souvent la bonne réponse pour les cas complexes. C'est l'operator le plus flexible pour gérer les dépendances réactives.