Skip to content

concatWith - Combiner séquentiellement des flux dans un pipeline

L'opérateur concatWith combine séquentiellement les autres Observables spécifiés après que l'Observable original soit complete. C'est la version Pipeable Operator de la Creation Function concat.

🔰 Syntaxe de base et utilisation

ts
import { of, delay } from 'rxjs';
import { concatWith } from 'rxjs';

const obs1$ = of('A', 'B').pipe(delay(100));
const obs2$ = of('C', 'D').pipe(delay(100));
const obs3$ = of('E', 'F').pipe(delay(100));

obs1$
  .pipe(concatWith(obs2$, obs3$))
  .subscribe(console.log);

// Sortie: A → B → C → D → E → F
  • obs2$ commence après la complétion de obs1$, et obs3$ commence après la complétion de obs2$.
  • Peut être utilisé dans une chaîne .pipe(), facilitant la combinaison avec d'autres opérateurs.

🌐 Documentation officielle RxJS - concatWith

💡 Patterns d'utilisation typiques

  • Traitement séquentiel dans un pipeline : Combiner séquentiellement des données supplémentaires à un flux transformé
  • Traitement de suivi après complétion : Ajouter un nettoyage ou une notification après le traitement principal
  • Chargement progressif de données : Récupérer séquentiellement des données supplémentaires après l'obtention des données initiales

🧠 Exemple de code pratique (avec UI)

Un exemple qui affiche les résultats de recherche principaux, puis affiche séquentiellement les articles recommandés.

ts
import { of, delay } from 'rxjs';
import { concatWith, map } from 'rxjs';

// Création de la zone de sortie
const output = document.createElement('div');
output.innerHTML = '<h3>Exemple pratique de concatWith :</h3>';
document.body.appendChild(output);

// Résultats de recherche principaux
const searchResults$ = of('🔍 Résultat 1', '🔍 Résultat 2', '🔍 Résultat 3').pipe(
  delay(500)
);

// Recommandations 1
const recommendations1$ = of('💡 Produit recommandé A', '💡 Produit recommandé B').pipe(
  delay(300)
);

// Recommandations 2
const recommendations2$ = of('⭐ Produit populaire X', '⭐ Produit populaire Y').pipe(
  delay(300)
);

// Combiner séquentiellement et afficher
searchResults$
  .pipe(
    concatWith(recommendations1$, recommendations2$),
    map((value, index) => `${index + 1}. ${value}`)
  )
  .subscribe((value) => {
    const item = document.createElement('div');
    item.textContent = value;
    output.appendChild(item);
  });
  • Les résultats de recherche s'affichent d'abord,
  • Puis les produits recommandés s'affichent séquentiellement.
  • Peut être combiné avec d'autres opérateurs comme map dans le pipeline.

🔄 Différence avec la Creation Function concat

Différences de base

concat (Creation Function)concatWith (Pipeable Operator)
Lieu d'utilisationUtilisé comme fonction indépendanteUtilisé dans la chaîne .pipe()
Syntaxeconcat(obs1$, obs2$, obs3$)obs1$.pipe(concatWith(obs2$, obs3$))
Premier fluxTraité égalementTraité comme flux principal
AvantageSimple et lisibleFacile à combiner avec d'autres opérateurs

Exemples concrets de choix

Pour une simple combinaison, la Creation Function est recommandée

ts
import { concat, of } from 'rxjs';

const part1$ = of('A', 'B');
const part2$ = of('C', 'D');
const part3$ = of('E', 'F');

// Simple et lisible
concat(part1$, part2$, part3$).subscribe(console.log);
// Sortie: A → B → C → D → E → F

Quand une transformation est nécessaire en cours de route, le Pipeable Operator est recommandé

ts
import { of } from 'rxjs';
import { concatWith, map, filter } from 'rxjs';

const userData$ = of({ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 });
const additionalData$ = of({ name: 'Charlie', age: 35 });

// ✅ Version Pipeable Operator - complète en un seul pipeline
userData$
  .pipe(
    filter(user => user.age >= 30),  // 30 ans et plus seulement
    map(user => user.name),          // Extraire le nom uniquement
    concatWith(
      additionalData$.pipe(map(user => user.name))
    )
  )
  .subscribe(console.log);
// Sortie: Alice → Charlie

Pour ajouter un traitement de suivi au flux principal

ts
import { fromEvent, of } from 'rxjs';
import { concatWith, take, mapTo } from 'rxjs';

// Création du bouton et de la zone de sortie
const button = document.createElement('button');
button.textContent = 'Cliquez 3 fois';
document.body.appendChild(button);

const output = document.createElement('div');
output.style.marginTop = '10px';
document.body.appendChild(output);

const clicks$ = fromEvent(button, 'click');

// ✅ Version Pipeable Operator - naturel comme extension du flux principal
clicks$
  .pipe(
    take(3),                          // Obtenir les 3 premiers clics
    mapTo('Cliqué'),
    concatWith(of('Terminé'))    // Message supplémentaire après complétion
  )
  .subscribe(message => {
    const div = document.createElement('div');
    div.textContent = message;
    output.appendChild(div);
  });

Résumé

  • concat : Optimal pour simplement combiner plusieurs flux
  • concatWith : Optimal quand vous voulez ajouter des transformations au flux principal et ajouter des suites

⚠️ Points d'attention

Délai dû à l'attente de complétion

L'Observable suivant ne démarre pas tant que l'original n'est pas terminé.

ts
import { interval, of } from 'rxjs';
import { concatWith, take } from 'rxjs';

interval(1000).pipe(
  take(3),              // Terminer après 3
  concatWith(of('Terminé'))
).subscribe(console.log);
// Sortie: 0 → 1 → 2 → Terminé

Gestion des erreurs

Si une erreur se produit dans l'Observable précédent, les Observables suivants ne sont pas exécutés.

ts
import { throwError, of } from 'rxjs';
import { concatWith, catchError } from 'rxjs';

throwError(() => new Error('Erreur survenue'))
  .pipe(
    catchError(err => of('Erreur récupérée')),
    concatWith(of('Traitement suivant'))
  )
  .subscribe(console.log);
// Sortie: Erreur récupérée → Traitement suivant

📚 Opérateurs associés

  • concat - Version Creation Function
  • mergeWith - Version Pipeable pour combinaison parallèle
  • concatMap - Mapping séquentiel de chaque valeur

Publié sous licence CC-BY-4.0.