La confusi�n de selecci�n de operadores
RxJS tiene m�s de 100 tipos de operadores, y dudar sobre cu�l usar es una dificultad que todos experimentan. Esta p�gina proporciona criterios de selecci�n pr�cticos y diagramas de flujo.
Criterios para elegir entre m�s de 100 operadores
Problema: Demasiadas opciones
// Quiero transformar un array... �map? �scan? �reduce? �toArray?
// Quiero llamar m�ltiples APIs... �mergeMap? �switchMap? �concatMap? �exhaustMap?
// Quiero filtrar valores... �filter? �take? �first? �distinctUntilChanged?
// Quiero combinar m�ltiples streams... �merge? �combineLatest? �zip? �forkJoin?Soluci�n: Acotar por categor�a + prop�sito
Diagrama de flujo de selecci�n m�s detallado
El siguiente diagrama de flujo muestra el procedimiento para elegir operadores seg�n prop�sitos espec�ficos.
1. Operadores de transformaci�n (Transformation)
�Cu�ndo usar? Cuando quieres cambiar la forma de los datos, llamar procesamiento as�ncrono
| Operador | Prop�sito | Casos de uso comunes |
|---|---|---|
| map | Transformaci�n 1:1 de valores | Obtener propiedades, c�lculos, conversi�n de tipos |
| scan | Procesamiento acumulativo (fluye valores intermedios) | Contador, suma, historial |
| reduce | Procesamiento acumulativo (solo valor final) | Suma de array, valor m�ximo |
| mergeMap | Ejecuci�n paralela de procesamiento as�ncrono | Llamadas paralelas a m�ltiples APIs |
| switchMap | Cambiar procesamiento as�ncrono | API de b�squeda (solo el m�s reciente) |
| concatMap | Ejecuci�n secuencial de procesamiento as�ncrono | Procesamiento donde el orden es importante |
| exhaustMap | Ignorar nuevo procesamiento durante ejecuci�n | Prevenci�n de clics m�ltiples (bot�n enviar) |
Ejemplo pr�ctico: Selecci�n por caso de uso
Caso de uso 1: Obtener propiedad
import { of } from 'rxjs';
import { map } from 'rxjs';
interface User { id: number; name: string; }
of({ id: 1, name: 'Alice' }).pipe(
map(user => user.name) // Transformaci�n 1:1 de valor � map
).subscribe(name => console.log(name)); // 'Alice'Caso de uso 2: Contador
import { fromEvent } from 'rxjs';
import { scan } from 'rxjs';
const button = document.querySelector('button')!;
fromEvent(button, 'click').pipe(
scan(count => count + 1, 0) // Procesamiento acumulativo � scan
).subscribe(count => console.log(`N�mero de clics: ${count}`));Caso de uso 3: Llamada a API de b�squeda
import { fromEvent } from 'rxjs';
import { debounceTime, map, switchMap } from 'rxjs';
const searchInput = document.querySelector('input')!;
fromEvent(searchInput, 'input').pipe(
debounceTime(300),
map(e => (e.target as HTMLInputElement).value),
switchMap(query => searchAPI(query)) // Solo el m�s reciente � switchMap
).subscribe(results => console.log(results));2. Operadores de filtrado (Filtering)
�Cu�ndo usar?
Cuando quieres seleccionar valores, controlar el timing
| Operador | Prop�sito | Casos de uso comunes |
|---|---|---|
| filter | Pasar solo valores que cumplen condici�n | Solo n�meros pares, solo valores no nulos |
| take | Solo los primeros N | Obtener primeros 5 elementos |
| first | Solo el primero | Obtener valor inicial |
| distinctUntilChanged | Solo valores diferentes del anterior | Excluir duplicados |
| debounceTime | Emitir despu�s de tiempo transcurrido | Entrada de b�squeda (despu�s de completar entrada) |
| throttleTime | Reducir a intervalos regulares | Evento de scroll |
Ejemplo pr�ctico: Selecci�n por caso de uso
Caso de uso 1: Obtener solo n�meros pares
import { of } from 'rxjs';
import { filter } from 'rxjs';
of(1, 2, 3, 4, 5).pipe(
filter(n => n % 2 === 0) // Solo valores que cumplen condici�n � filter
).subscribe(console.log); // 2, 4Caso de uso 2: Optimizaci�n de entrada de b�squeda
import { fromEvent } from 'rxjs';
import { debounceTime, distinctUntilChanged, map } from 'rxjs';
const input = document.querySelector('input')!;
fromEvent(input, 'input').pipe(
debounceTime(300), // Esperar finalizaci�n de entrada � debounceTime
map(e => (e.target as HTMLInputElement).value),
distinctUntilChanged() // Excluir duplicados � distinctUntilChanged
).subscribe(query => console.log('B�squeda:', query));Caso de uso 3: Reducci�n de eventos de scroll
import { fromEvent } from 'rxjs';
import { throttleTime } from 'rxjs';
fromEvent(window, 'scroll').pipe(
throttleTime(200) // Solo una vez cada 200ms � throttleTime
).subscribe(() => console.log('Posici�n de scroll:', window.scrollY));3. Operadores de combinaci�n (Combination)
�Cu�ndo usar?
Cuando quieres combinar m�ltiples streams
| Operador | Prop�sito | Casos de uso comunes |
|---|---|---|
| merge | M�ltiples streams en paralelo | Monitoreo de m�ltiples eventos |
| combineLatest | Combinar todos los valores m�s recientes | Validaci�n de formularios |
| zip | Emparejar valores correspondientes | Relacionar resultados de 2 APIs |
| forkJoin | Resultados en array despu�s de finalizaci�n completa | Ejecuci�n paralela de m�ltiples APIs |
| withLatestFrom | Stream principal + valor auxiliar | Evento + estado actual |
Ejemplo pr�ctico: Selecci�n por caso de uso
Caso de uso 1: Monitorear m�ltiples eventos
import { fromEvent, merge } from 'rxjs';
const clicks$ = fromEvent(document, 'click');
const keypresses$ = fromEvent(document, 'keypress');
merge(clicks$, keypresses$).pipe() // Monitoreo paralelo � merge
.subscribe(() => console.log('Ocurri� alg�n evento'));Caso de uso 2: Validaci�n de formularios
import { combineLatest } from 'rxjs';
import { map } from 'rxjs';
const email$ = getFormControl('email');
const password$ = getFormControl('password');
combineLatest([email$, password$]).pipe( // Todos los valores m�s recientes � combineLatest
map(([email, password]) => email.length > 0 && password.length > 7)
).subscribe(isValid => console.log('Formulario v�lido:', isValid));Caso de uso 3: Ejecuci�n paralela de m�ltiples APIs
import { forkJoin } from 'rxjs';
forkJoin({
user: getUserAPI(),
posts: getPostsAPI(),
comments: getCommentsAPI()
}).subscribe(({ user, posts, comments }) => { // Espera finalizaci�n completa � forkJoin
console.log('Obtenci�n completa de todos los datos', { user, posts, comments });
});Los 20 operadores m�s usados
Los siguientes son los operadores m�s utilizados frecuentemente en el trabajo. Primero domina estos 20.
>G M�s frecuentes (obligatorios)
- map - Transformar valores
- filter - Filtrar por condici�n
- switchMap - B�squeda, etc., solo necesario el m�s reciente
- tap - Depuraci�n, efectos secundarios
- take - Primeros N
- first - Primero 1
- catchError - Manejo de errores
- takeUntil - Cancelar suscripci�n
>H Frecuentes (uso com�n)
- mergeMap - Procesamiento as�ncrono paralelo
- debounceTime - Esperar finalizaci�n de entrada
- distinctUntilChanged - Excluir duplicados
- combineLatest - Combinar m�ltiples valores
- startWith - Establecer valor inicial
- scan - Procesamiento acumulativo
- shareReplay - Cachear resultados
>I Uso com�n (deber�as conocer)
- concatMap - Procesamiento secuencial
- throttleTime - Reducci�n de eventos
- withLatestFrom - Obtener valor auxiliar
- forkJoin - Espera de m�ltiples APIs
- retry - Procesamiento de reintento
switchMap vs mergeMap vs concatMap vs exhaustMap
Estos 4 son los operadores m�s confundidos. Entendamos claramente sus diferencias.
Tabla comparativa
| Operador | M�todo de ejecuci�n | Procesamiento anterior | Nuevo procesamiento | D�nde usar |
|---|---|---|---|---|
| switchMap | Cambiar | Cancelar | Iniciar inmediatamente | B�squeda, autocompletar |
| mergeMap | Ejecuci�n paralela | Continuar | Iniciar inmediatamente | Subida de archivos, an�lisis |
| concatMap | Ejecuci�n secuencial | Esperar finalizaci�n | Iniciar despu�s de esperar | Procesamiento donde el orden es importante |
| exhaustMap | Ignorar durante ejecuci�n | Continuar | Ignorar | Prevenci�n de clics m�ltiples en bot�n |
Comparaci�n con diagramas de m�rmol
Exterior: ----A----B----C----|
Interior: A � --1--2|
B � --3--4|
C � --5--6|
switchMap: ----1--3--5--6| (A se cancela antes de 2, B se cancela antes de 4)
mergeMap: ----1-23-45-6| (todo ejecuci�n paralela)
concatMap: ----1--2--3--4--5--6| (ejecuci�n secuencial)
exhaustMap: ----1--2| (B, C se ignoran)Ejemplo pr�ctico: Diferencia de los 4 en el mismo procesamiento
Situaci�n: Llamar API (tarda 1 segundo) con cada clic del bot�n. Usuario hace clic cada 0.5 segundos.
switchMap - �ptimo para b�squeda
import { fromEvent } from 'rxjs';
import { switchMap } from 'rxjs';
fromEvent(button, 'click').pipe(
switchMap(() => searchAPI()) // Solo ejecutar el m�s reciente, cancelar solicitudes antiguas
).subscribe(result => console.log(result));
// 0.0 seg: Clic1 � Inicio API1
// 0.5 seg: Clic2 � Cancelar API1, Inicio API2
// 1.0 seg: Clic3 � Cancelar API2, Inicio API3
// 2.0 seg: Completar API3 � Mostrar resultado (solo API3)=� D�nde usar
- B�squeda/autocompletar: Solo necesario el valor de entrada m�s reciente
- Cambio de pesta�as: Solo necesarios datos de la pesta�a mostrada
- Paginaci�n: Solo mostrar la p�gina m�s reciente
mergeMap - �ptimo para procesamiento paralelo
import { fromEvent } from 'rxjs';
import { mergeMap } from 'rxjs';
fromEvent(button, 'click').pipe(
mergeMap(() => uploadFileAPI()) // Todo ejecuci�n paralela
).subscribe(result => console.log(result));
// 0.0 seg: Clic1 � Inicio API1
// 0.5 seg: Clic2 � Inicio API2 (API1 contin�a)
// 1.0 seg: Clic3 � Inicio API3 (API1, API2 contin�an)
// 1.0 seg: Completar API1 � Mostrar resultado
// 1.5 seg: Completar API2 � Mostrar resultado
// 2.0 seg: Completar API3 � Mostrar resultado=� D�nde usar
- Subida de archivos: Subir m�ltiples archivos simult�neamente
- An�lisis/env�o de logs: Ejecutar procesamientos independientes en paralelo
- Sistema de notificaciones: Procesar m�ltiples notificaciones simult�neamente
concatMap - �ptimo para procesamiento donde el orden es importante
import { fromEvent } from 'rxjs';
import { concatMap } from 'rxjs';
fromEvent(button, 'click').pipe(
concatMap(() => updateDatabaseAPI()) // Ejecuci�n secuencial (esperar finalizaci�n anterior)
).subscribe(result => console.log(result));
// 0.0 seg: Clic1 � Inicio API1
// 0.5 seg: Clic2 � Esperar (a�adir a cola)
// 1.0 seg: Clic3 � Esperar (a�adir a cola)
// 1.0 seg: Completar API1 � Mostrar resultado, Inicio API2
// 2.0 seg: Completar API2 � Mostrar resultado, Inicio API3
// 3.0 seg: Completar API3 � Mostrar resultado=� D�nde usar
- Actualizaci�n de base de datos: Procesamiento de escritura donde el orden es importante
- Transacciones: Usar resultado del procesamiento anterior en el siguiente
- Animaciones: Procesos que quieres ejecutar en orden
exhaustMap - �ptimo para prevenci�n de clics m�ltiples
import { fromEvent } from 'rxjs';
import { exhaustMap } from 'rxjs';
fromEvent(button, 'click').pipe(
exhaustMap(() => submitFormAPI()) // Ignorar nuevas solicitudes durante ejecuci�n
).subscribe(result => console.log(result));
// 0.0 seg: Clic1 � Inicio API1
// 0.5 seg: Clic2 � Ignorar (API1 en ejecuci�n)
// 1.0 seg: Clic3 � Ignorar (API1 en ejecuci�n)
// 1.0 seg: Completar API1 � Mostrar resultado
// 1.5 seg: Clic4 � Inicio API4 (anterior ya completado)=� D�nde usar
- Bot�n enviar: Prevenci�n de env�o doble
- Procesamiento de login: Prevenir errores por clics m�ltiples
- Procesamiento de pago: Prevenir ejecuci�n duplicada
Diagrama de flujo de selecci�n
Criterios de decisi�n en la pr�ctica
Paso 1: Clarificar qu� quieres lograr
// L Mal ejemplo: Usar mergeMap sin m�s
observable$.pipe(
mergeMap(value => someAPI(value))
);
// Buen ejemplo: Elegir despu�s de clarificar el prop�sito
// Prop�sito: Para la entrada de b�squeda del usuario, quiero mostrar solo el resultado m�s reciente
// � Deber�a cancelar solicitudes antiguas � switchMap
searchInput$.pipe(
switchMap(query => searchAPI(query))
);Paso 2: Considerar el rendimiento
Elecci�n de debounceTime vs throttleTime
// Entrada de b�squeda: Ejecutar despu�s de que el usuario "complete" la entrada
searchInput$.pipe(
debounceTime(300), // Ejecutar si no hay entrada durante 300ms
switchMap(query => searchAPI(query))
);
// Scroll: Ejecutar a intervalos regulares (prevenir alta frecuencia)
scroll$.pipe(
throttleTime(200), // Solo ejecutar una vez cada 200ms
tap(() => loadMoreItems())
);Paso 3: Incorporar manejo de errores
import { of } from 'rxjs';
import { catchError, retry, switchMap } from 'rxjs';
searchInput$.pipe(
debounceTime(300),
switchMap(query =>
searchAPI(query).pipe(
retry(2), // Reintentar hasta 2 veces
catchError(err => {
console.error('Error de b�squeda:', err);
return of([]); // Devolver array vac�o
})
)
)
).subscribe(results => console.log(results));Paso 4: Prevenir fugas de memoria
import { Subject } from 'rxjs';
import { switchMap, takeUntil } from 'rxjs';
class SearchComponent {
private destroy$ = new Subject<void>();
ngOnInit() {
searchInput$.pipe(
debounceTime(300),
switchMap(query => searchAPI(query)),
takeUntil(this.destroy$) // Cancelar al destruir componente
).subscribe(results => console.log(results));
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}Lista de verificaci�n de comprensi�n
Verifica si puedes responder a las siguientes preguntas.
## Comprensi�n b�sica
- [ ] Puedo clasificar operadores por categor�a (transformaci�n, filtrado, combinaci�n)
- [ ] Puedo explicar m�s de 10 de los 20 operadores m�s usados
- [ ] Puedo explicar las diferencias entre switchMap, mergeMap, concatMap, exhaustMap
## Selecci�n pr�ctica
- [ ] Puedo elegir operadores adecuados para funci�n de b�squeda (switchMap + debounceTime)
- [ ] Puedo elegir operadores adecuados para llamadas paralelas a m�ltiples APIs (forkJoin or mergeMap)
- [ ] Puedo elegir operadores adecuados para validaci�n de formularios (combineLatest)
## Rendimiento
- [ ] Puedo diferenciar el uso de debounceTime y throttleTime
- [ ] Conozco m�todos de optimizaci�n para eventos de alta frecuencia
- [ ] Puedo implementar patrones para prevenir fugas de memoria
## Manejo de errores
- [ ] Puedo usar catchError y retry en combinaci�n
- [ ] Puedo implementar procesamiento de fallback en caso de error
- [ ] Puedo dar feedback de errores al usuarioPr�ximos pasos
Una vez que entiendas la selecci�n de operadores, aprende sobre timing y orden.
� Comprensi�n de timing y orden (en preparaci�n) - Cu�ndo fluyen los valores, comprensi�n de sincron�a vs asincron�a
P�ginas relacionadas
- Chapter 4: Comprensi�n de operadores - Detalles de todos los operadores
- Chapter 13: Colecci�n de patrones pr�cticos - Casos de uso reales (en preparaci�n)
- Chapter 10: Errores comunes y soluciones - Antipatrones de selecci�n inadecuada de operadores
<� Ejercicios de pr�ctica
Problema 1: Seleccionar operador adecuado
Elige el operador m�s �ptimo para los siguientes escenarios.
- Usuario ingresa en caja de b�squeda � Llamada a API
- Clic de bot�n para subir m�ltiples archivos
- Determinar si todos los campos del formulario son v�lidos
- Prevenir clics m�ltiples en bot�n enviar
Ejemplo de respuesta
1. Caja de b�squeda � Llamada a API
searchInput$.pipe(
debounceTime(300), // Esperar finalizaci�n de entrada
distinctUntilChanged(), // Excluir duplicados
switchMap(query => searchAPI(query)) // Solo el m�s reciente
).subscribe(results => displayResults(results));Raz�n
La b�squeda solo necesita el resultado m�s reciente, por lo que switchMap. Espera finalizaci�n de entrada con debounceTime.
2. Subir m�ltiples archivos
fromEvent(uploadButton, 'click').pipe(
mergeMap(() => {
const files = getSelectedFiles();
return forkJoin(files.map(file => uploadFileAPI(file)));
})
).subscribe(results => console.log('Subida completa de todos los archivos', results));Raz�n
Para subir m�ltiples archivos en paralelo, forkJoin. Tambi�n es posible mergeMap para procesamientos independientes.
3. Validez de todos los campos del formulario
combineLatest([
emailField$,
passwordField$,
agreeTerms$
]).pipe(
map(([email, password, agreed]) =>
email.valid && password.valid && agreed
)
).subscribe(isValid => submitButton.disabled = !isValid);Raz�n
Para combinar los valores m�s recientes de todos los campos, combineLatest.
4. Prevenci�n de clics m�ltiples en bot�n enviar
fromEvent(submitButton, 'click').pipe(
exhaustMap(() => submitFormAPI())
).subscribe(result => console.log('Env�o completado', result));Raz�n
Para proteger el procesamiento en ejecuci�n e ignorar nuevos clics, exhaustMap.
Problema 2: Elecci�n de switchMap y mergeMap
El siguiente c�digo usa mergeMap, pero hay un problema. Corr�gelo.
searchInput$.pipe(
debounceTime(300),
mergeMap(query => searchAPI(query))
).subscribe(results => displayResults(results));Ejemplo de respuesta
searchInput$.pipe(
debounceTime(300),
switchMap(query => searchAPI(query)) // mergeMap � switchMap
).subscribe(results => displayResults(results));Problema
- Con
mergeMap, todas las solicitudes de b�squeda se ejecutan en paralelo - Si el usuario ingresa "a"�"ab"�"abc", se ejecutan las 3 solicitudes
- Solicitudes antiguas (resultado de "a") pueden regresar despu�s y sobrescribir el resultado m�s reciente
Raz�n de correcci�n
- Usando
switchMap, cuando comienza una nueva b�squeda, se cancelan las solicitudes antiguas - Siempre se muestra solo el resultado de b�squeda m�s reciente
Problema 3: Escenario pr�ctico
Escribe c�digo que cumpla los siguientes requisitos.
Puntos clave
- Usuario hace clic en bot�n
- Obtener en paralelo 3 APIs (informaci�n de usuario, lista de posts, lista de comentarios)
- Mostrar datos cuando todo est� completo
- Si ocurre error, devolver datos vac�os
- Cancelar suscripci�n al destruir componente
Ejemplo de respuesta
import { fromEvent, forkJoin, of, Subject } from 'rxjs';
import { switchMap, catchError, takeUntil } from 'rxjs';
class DataComponent {
private destroy$ = new Subject<void>();
private button = document.querySelector('button')!;
ngOnInit() {
fromEvent(this.button, 'click').pipe(
switchMap(() =>
forkJoin({
user: this.getUserAPI().pipe(
catchError(() => of(null))
),
posts: this.getPostsAPI().pipe(
catchError(() => of([]))
),
comments: this.getCommentsAPI().pipe(
catchError(() => of([]))
)
})
),
takeUntil(this.destroy$)
).subscribe(({ user, posts, comments }) => {
console.log('Obtenci�n de datos completada', { user, posts, comments });
});
}
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
private getUserAPI() { /* ... */ }
private getPostsAPI() { /* ... */ }
private getCommentsAPI() { /* ... */ }
}Puntos
forkJoinejecuta 3 APIs en paralelo y espera finalizaci�n completa- Establecer valor de fallback en caso de error con
catchErroren cada API switchMapcambia a nueva solicitud con cada clic del bot�n- Cancelaci�n autom�tica al destruir componente con
takeUntil