Skip to content

concatWith - Concatenar en Secuencia

El operador concatWith concatena secuencialmente los otros Observables especificados después de que el Observable original completa. Esta es la versión Pipeable Operator de la Creation Function concat.

🔰 Sintaxis Básica y Uso

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);

// Salida: A → B → C → D → E → F
  • Después de que obs1$ completa, obs2$ inicia, y después de que obs2$ completa, obs3$ inicia.
  • Se puede usar dentro de cadenas .pipe(), facilitando la combinación con otros operadores.

🌐 Documentación Oficial de RxJS - concatWith

💡 Patrones de Uso Típicos

  • Procesamiento secuencial dentro de un pipeline: Combinar datos adicionales en secuencia al stream transformado
  • Procesamiento de seguimiento después de la completación: Agregar limpieza y notificaciones después de que el procesamiento principal completa
  • Carga de datos por etapas: Adquirir datos adicionales secuencialmente después de la adquisición de datos iniciales

🧠 Ejemplo de Código Práctico (con UI)

Ejemplo de mostrar elementos recomendados relacionados en orden después de mostrar los resultados de búsqueda principales.

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

// Crear área de salida
const output = document.createElement('div');
output.innerHTML = '<h3>Ejemplo Práctico de concatWith:</h3>';
document.body.appendChild(output);

// Resultados de búsqueda principales
const searchResults$ = of('🔍 Resultado de Búsqueda 1', '🔍 Resultado de Búsqueda 2', '🔍 Resultado de Búsqueda 3').pipe(
  delay(500)
);

// Elementos recomendados 1
const recommendations1$ = of('💡 Producto Recomendado A', '💡 Producto Recomendado B').pipe(
  delay(300)
);

// Elementos recomendados 2
const recommendations2$ = of('⭐ Producto Popular X', '⭐ Producto Popular Y').pipe(
  delay(300)
);

// Combinar en secuencia y mostrar
searchResults$
  .pipe(
    concatWith(recommendations1$, recommendations2$),
    map((value, index) => `${index + 1}. ${value}`)
  )
  .subscribe((value) => {
    const item = document.createElement('div');
    item.textContent = value;
    output.appendChild(item);
  });
  • Los resultados de búsqueda se muestran primero,
  • Luego los productos recomendados se muestran en orden.
  • Se puede usar en combinación con otros operadores como map dentro del pipeline.

🔄 Diferencia con la Creation Function concat

Diferencias Básicas

concat (Creation Function)concatWith (Pipeable Operator)
Ubicación de UsoUsado como función independienteUsado dentro de cadena .pipe()
Sintaxisconcat(obs1$, obs2$, obs3$)obs1$.pipe(concatWith(obs2$, obs3$))
Primer StreamTrata todos por igualTrata como stream principal
VentajaSimple y legibleFácil de combinar con otros operadores

Ejemplos de Uso Específicos

Creation Function Recomendada Solo para Combinación Simple

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

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

// Simple y legible
concat(part1$, part2$, part3$).subscribe(console.log);
// Salida: A → B → C → D → E → F

Pipeable Operator Recomendado si Se Necesita Transformación

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 });

// ❌ Versión Creation Function - se vuelve verbosa
import { concat } from 'rxjs';
concat(
  userData$.pipe(
    filter(user => user.age >= 30),
    map(user => user.name)
  ),
  additionalData$.pipe(map(user => user.name))
).subscribe(console.log);

// ✅ Versión Pipeable Operator - completada en un pipeline
userData$
  .pipe(
    filter(user => user.age >= 30),  // Solo 30 años o más
    map(user => user.name),          // Extraer solo nombre
    concatWith(
      additionalData$.pipe(map(user => user.name))
    )
  )
  .subscribe(console.log);
// Salida: Alice → Charlie

Cuando Se Agrega Procesamiento Posterior al Stream Principal

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

// Crear botón y área de salida
const button = document.createElement('button');
button.textContent = 'Haz clic 3 veces';
document.body.appendChild(button);

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

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

// ✅ Versión Pipeable Operator - natural como extensión del stream principal
clicks$
  .pipe(
    take(3),                          // Obtener primeros 3 clics
    mapTo('Clicado'),
    concatWith(of('Completado'))      // Agregar mensaje después de completación
  )
  .subscribe(message => {
    const div = document.createElement('div');
    div.textContent = message;
    output.appendChild(div);
  });

// Escribir el mismo comportamiento con versión Creation Function...
// ❌ Versión Creation Function - necesita separar stream principal
import { concat } from 'rxjs';
concat(
  clicks$.pipe(
    take(3),
    mapTo('Clicado')
  ),
  of('Completado')
).subscribe(console.log);

Resumen

  • concat: Óptimo para simplemente combinar múltiples streams
  • concatWith: Óptimo cuando se desea agregar procesamiento posterior al stream principal mientras se transforma o procesa

⚠️ Notas Importantes

Retraso Debido a Esperar la Completación

El siguiente Observable no iniciará hasta que el Observable original complete.

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

interval(1000).pipe(
  take(3),              // Completar con 3 valores
  concatWith(of('Completo'))
).subscribe(console.log);
// Salida: 0 → 1 → 2 → Completo

Manejo de Errores

Si ocurre un error en el Observable anterior, los Observables posteriores no se ejecutarán.

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

throwError(() => new Error('Ocurrió un error'))
  .pipe(
    catchError(err => of('Error recuperado')),
    concatWith(of('Siguiente proceso'))
  )
  .subscribe(console.log);
// Salida: Error recuperado → Siguiente proceso

📚 Operadores Relacionados

  • concat - Versión Creation Function
  • mergeWith - Versión Pipeable para combinación paralela
  • concatMap - Mapear cada valor secuencialmente

Publicado bajo licencia CC-BY-4.0.