Skip to content

La barrera de la depuraci�n

Cuando te enfrentas a problemas en RxJS como "los valores no fluyen", "aparecen valores diferentes a los esperados" o "podr�a haber una fuga de memoria", resolverlos puede llevar mucho tiempo si no conoces los m�todos de depuraci�n adecuados. Esta p�gina explica de manera integral las t�cnicas de depuraci�n espec�ficas de RxJS.

Estrategia b�sica de depuraci�n en RxJS

Los 5 pasos de la depuraci�n

Paso 1: Identificar el problema

Primero, clarifica qu� es el problema.

S�ntomaPosibles causas
No fluyen valoresOlvidaste subscribe, termina antes de complete, excluido por filter
No aparece el primer valorcombineLatest no cumple condici�n inicial, BehaviorSubject no configurado
El orden es extra�oUso de mergeMap, timing as�ncrono
Aparecen valores duplicadosM�ltiples subscribe sin share, mal uso de shareReplay
Hay fuga de memoriaOlvidaste unsubscribe, shareReplay con refCount: false
Los valores se retrasandebounceTime, throttleTime, procesamiento as�ncrono

Paso 2: Formular hip�tesis

Sup�n la causa del problema.

typescript
// Ejemplo: Problema de "no aparecen valores"
// Hip�tesis 1: �No hay subscribe?
// Hip�tesis 2: �complete/error demasiado temprano?
// Hip�tesis 3: �Excluido por filter?
// Hip�tesis 4: �Demora por procesamiento as�ncrono?

Paso 3: Verificar con tap

Inserta tap en cada etapa para confirmar qu� est� ocurriendo realmente.

typescript
import { of } from 'rxjs';
import { map, filter, tap } from 'rxjs';

of(1, 2, 3, 4, 5).pipe(
  tap(v => console.log('=5 Entrada:', v)),
  filter(x => x > 10), // L Todos son excluidos
  tap(v => console.log(' Pas� filter:', v)),
  map(x => x * 10),
  tap(v => console.log('=� Despu�s de map:', v))
).subscribe(result => {
  console.log('=� Resultado:', result);
});

// Salida:
// =5 Entrada: 1
// =5 Entrada: 2
// =5 Entrada: 3
// =5 Entrada: 4
// =5 Entrada: 5
// (Ninguno pas� filter � filter es la causa)

Escenarios comunes de depuraci�n

Escenario 1: Los valores no fluyen

Problema 1-1: Olvidaste subscribe

L Mal ejemplo: No hay subscribe

typescript
import { of } from 'rxjs';
import { map } from 'rxjs';

const result$ = of(1, 2, 3).pipe(
  map(x => x * 10)
);

console.log('Completado'); // Se imprime inmediatamente
// No fluyen valores a result$ (porque no hay subscribe)

 Buen ejemplo: Hacer subscribe

typescript
import { of } from 'rxjs';
import { map } from 'rxjs';

const result$ = of(1, 2, 3).pipe(
  map(x => x * 10)
);

result$.subscribe(value => {
  console.log('Valor:', value);
});

console.log('Completado');

// Salida:
// Valor: 10
// Valor: 20
// Valor: 30
// Completado

Puntos de verificaci�n

  • Definir un Observable no hace nada
  • Debes hacer subscribe obligatoriamente
  • Si usas async pipe, subscribe no es necesario (Angular, etc.)

Problema 1-2: complete/error llega demasiado pronto

L Mal ejemplo: complete llega primero

typescript
import { EMPTY } from 'rxjs';
import { map } from 'rxjs';

EMPTY.pipe( // L complete inmediatamente
  map(x => x * 10)
).subscribe({
  next: value => console.log('Valor:', value),
  complete: () => console.log('Completado')
});

// Salida:
// Completado
// (No fluye ning�n valor)

 Buen ejemplo: Verificar con tap

typescript
import { EMPTY } from 'rxjs';
import { map, tap } from 'rxjs';

EMPTY.pipe(
  tap(() => console.log('=A Lleg� valor')), // Esto no se imprime
  map(x => x * 10)
).subscribe({
  next: value => console.log('Valor:', value),
  complete: () => console.log('Completado')
});

// Salida:
// Completado
// (tap tampoco se ejecuta � EMPTY es la causa)

Problema 1-3: Excluido por filter

L Mal ejemplo: Excluir todo sin darse cuenta

typescript
import { of } from 'rxjs';
import { filter } from 'rxjs';

of(1, 2, 3, 4, 5).pipe(
  filter(x => x > 100) // L Todos excluidos
).subscribe(value => {
  console.log('Valor:', value); // No se imprime nada
});

 Buen ejemplo: Verificar con tap

typescript
import { of } from 'rxjs';
import { filter, tap } from 'rxjs';

of(1, 2, 3, 4, 5).pipe(
  tap(v => console.log('Antes de filter:', v)),
  filter(x => x > 100),
  tap(v => console.log('Despu�s de filter:', v)) // No se imprime ninguno
).subscribe(value => {
  console.log('Valor:', value);
});

// Salida:
// Antes de filter: 1
// Antes de filter: 2
// Antes de filter: 3
// Antes de filter: 4
// Antes de filter: 5
// (No hay ninguno despu�s de filter � filter es demasiado estricto)

Escenario 2: Aparecen valores diferentes a los esperados

Problema 2-1: Error de conversi�n de tipo

L Mal ejemplo: Confusi�n entre string y n�mero

typescript
import { of } from 'rxjs';
import { map } from 'rxjs';

const input = '5'; // string

of(input).pipe(
  map(x => x + 10) // L '5' + 10 = '510' (concatenaci�n de strings)
).subscribe(result => {
  console.log('Resultado:', result); // Resultado: 510
  console.log('Tipo:', typeof result); // Tipo: string
});

 Buen ejemplo: Verificar tipo con tap

typescript
import { of } from 'rxjs';
import { map, tap } from 'rxjs';

const input = '5';

of(input).pipe(
  tap(x => console.log('Entrada:', x, typeof x)),
  map(x => Number(x)), // Convertir a n�mero
  tap(x => console.log('Despu�s de conversi�n:', x, typeof x)),
  map(x => x + 10)
).subscribe(result => {
  console.log('Resultado:', result); // Resultado: 15
});

Problema 2-2: Orden as�ncrono

L Mal ejemplo: El orden se desordena con mergeMap

typescript
import { of } from 'rxjs';
import { mergeMap, delay } from 'rxjs';

of(1, 2, 3).pipe(
  mergeMap(x =>
    of(x * 10).pipe(
      delay(Math.random() * 1000) // Retraso aleatorio
    )
  )
).subscribe(value => {
  console.log('Valor:', value);
});

// Ejemplo de salida (el orden no est� garantizado):
// Valor: 20
// Valor: 10
// Valor: 30

 Buen ejemplo: Garantizar el orden con concatMap

typescript
import { of } from 'rxjs';
import { concatMap, delay, tap } from 'rxjs';

of(1, 2, 3).pipe(
  tap(x => console.log('Entrada:', x)),
  concatMap(x =>
    of(x * 10).pipe(
      delay(Math.random() * 1000),
      tap(v => console.log('Completado:', v))
    )
  )
).subscribe(value => {
  console.log('Valor:', value);
});

// Salida (siempre en este orden):
// Entrada: 1
// Completado: 10
// Valor: 10
// Entrada: 2
// Completado: 20
// Valor: 20
// Entrada: 3
// Completado: 30
// Valor: 30

Escenario 3: Detecci�n de fugas de memoria

Problema 3-1: Olvidaste unsubscribe

L Mal ejemplo: No hacer unsubscribe

typescript
import { interval } from 'rxjs';

class Component {
  ngOnInit() {
    interval(1000).subscribe(n => {
      console.log('Valor:', n); // Se ejecuta eternamente
    });
  }

  ngOnDestroy() {
    // No hay unsubscribe � Fuga de memoria
  }
}

 Buen ejemplo: Cancelaci�n autom�tica con takeUntil

typescript
import { interval, Subject } from 'rxjs';
import { takeUntil } from 'rxjs';

class Component {
  private destroy$ = new Subject<void>();

  ngOnInit() {
    interval(1000).pipe(
      takeUntil(this.destroy$)
    ).subscribe(n => {
      console.log('Valor:', n);
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    console.log('Unsubscribe completado');
  }
}

Problema 3-2: Fuga de memoria con shareReplay

L Mal ejemplo: Fuga con refCount: false

typescript
import { interval } from 'rxjs';
import { shareReplay, take, tap } from 'rxjs';

const data$ = interval(1000).pipe(
  take(100),
  tap(n => console.log('Generando:', n)),
  shareReplay({ bufferSize: 1, refCount: false })
  // L refCount: false � Contin�a ejecut�ndose eternamente
);

const sub = data$.subscribe(n => console.log('Suscripci�n 1:', n));

setTimeout(() => {
  sub.unsubscribe();
  console.log('Unsubscribe hecho, pero contin�a ejecut�ndose internamente');
}, 5000);

 Buen ejemplo: Detenci�n autom�tica con refCount: true

typescript
import { interval } from 'rxjs';
import { shareReplay, take, tap } from 'rxjs';

const data$ = interval(1000).pipe(
  take(100),
  tap(n => console.log('Generando:', n)),
  shareReplay({ bufferSize: 1, refCount: true })
  //  refCount: true � Se detiene al cancelar todas las suscripciones
);

const sub = data$.subscribe(n => console.log('Suscripci�n 1:', n));

setTimeout(() => {
  sub.unsubscribe();
  console.log('Unsubscribe � El stream tambi�n se detiene');
}, 5000);

Herramientas y t�cnicas de depuraci�n

1. Depuraci�n gradual con tap

typescript
import { of } from 'rxjs';
import { map, filter, tap } from 'rxjs';

const debugTap = <T>(label: string, color: string = '=5') =>
  tap<T>({
    next: value => console.log(`${color} [${label}] next:`, value),
    error: error => console.error(`L [${label}] error:`, error),
    complete: () => console.log(` [${label}] complete`)
  });

of(1, 2, 3, 4, 5).pipe(
  debugTap('Entrada'),
  filter(x => x % 2 === 0),
  debugTap('Despu�s de filter', '=�'),
  map(x => x * 10),
  debugTap('Despu�s de map', '=�')
).subscribe({
  next: value => console.log('=� Resultado final:', value),
  complete: () => console.log('<� Completado')
});

2. Operador de depuraci�n personalizado

typescript
import { tap, timestamp, map } from 'rxjs';
import { MonoTypeOperatorFunction } from 'rxjs';

interface DebugOptions {
  label: string;
  showTimestamp?: boolean;
  showDiff?: boolean;
}

let lastTimestamp = 0;

function debug<T>(options: DebugOptions): MonoTypeOperatorFunction<T> {
  const { label, showTimestamp = true, showDiff = true } = options;

  return source => source.pipe(
    timestamp(),
    tap(({ value, timestamp }) => {
      const parts = [`[${label}]`, value];

      if (showTimestamp) {
        parts.push(`@${new Date(timestamp).toISOString()}`);
      }

      if (showDiff && lastTimestamp > 0) {
        const diff = timestamp - lastTimestamp;
        parts.push(`(+${diff}ms)`);
      }

      console.log(...parts);
      lastTimestamp = timestamp;
    }),
    map(({ value }) => value)
  );
}

// Uso
import { interval } from 'rxjs';
import { take } from 'rxjs';

interval(500).pipe(
  take(5),
  debug({ label: 'Temporizador' }),
  map(x => x * 10),
  debug({ label: 'Despu�s de transformaci�n', showDiff: false })
).subscribe();

3. RxJS DevTools (extensi�n de navegador)

M�todo de instalaci�n:

  1. Busca "RxJS DevTools" en Chrome/Edge Web Store
  2. A�ade la extensi�n
  3. Abre DevTools y haz clic en la pesta�a "RxJS"

Funciones principales:

  • Monitoreo en tiempo real de todos los Observables
  • Visualizaci�n con diagramas de m�rmol
  • Seguimiento de subscribe/unsubscribe
  • An�lisis de rendimiento

Ejemplo de uso:

typescript
import { interval } from 'rxjs';
import { map, take } from 'rxjs';

// Se detecta autom�ticamente en DevTools
const timer$ = interval(1000).pipe(
  take(10),
  map(x => x * 2)
);

timer$.subscribe(value => console.log(value));

4. Depuraci�n de errores

Identificar d�nde ocurre el error

typescript
import { of, throwError } from 'rxjs';
import { map, catchError, tap } from 'rxjs';

of(1, 2, 3).pipe(
  tap(v => console.log('1. Entrada:', v)),
  map(x => {
    if (x === 2) {
      throw new Error('No se puede usar 2');
    }
    return x * 10;
  }),
  tap(v => console.log('2. Despu�s de map:', v)), // No se ejecuta en caso de error
  catchError(error => {
    console.error('3. Error capturado:', error.message);
    return of(-1); // Devolver valor por defecto
  }),
  tap(v => console.log('4. Despu�s de catchError:', v))
).subscribe({
  next: value => console.log('5. Resultado:', value),
  error: error => console.error('Error de suscripci�n:', error),
  complete: () => console.log('6. Completado')
});

// Salida:
// 1. Entrada: 1
// 2. Despu�s de map: 10
// 5. Resultado: 10
// 1. Entrada: 2
// 3. Error capturado: No se puede usar 2
// 4. Despu�s de catchError: -1
// 5. Resultado: -1
// 6. Completado

Depuraci�n de rendimiento

Problema 1: Recalculaci�n excesiva

L Mal ejemplo: Recalculaci�n frecuente con combineLatest

typescript
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs';

const a$ = new BehaviorSubject(1);
const b$ = new BehaviorSubject(2);
const c$ = new BehaviorSubject(3);

combineLatest([a$, b$, c$]).pipe(
  map(([a, b, c]) => {
    console.log('Ejecutando c�lculo pesado'); // Se ejecuta frecuentemente
    return a + b + c;
  })
).subscribe(result => console.log('Resultado:', result));

// Actualizaci�n frecuente
setInterval(() => {
  a$.next(Math.random());
}, 100);

 Buen ejemplo: Excluir duplicados con distinctUntilChanged

typescript
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs';

const a$ = new BehaviorSubject(1);
const b$ = new BehaviorSubject(2);
const c$ = new BehaviorSubject(3);

combineLatest([a$, b$, c$]).pipe(
  map(([a, b, c]) => Math.floor(a) + Math.floor(b) + Math.floor(c)),
  distinctUntilChanged(), // Pasa solo cuando el valor cambia
  map(sum => {
    console.log('Ejecutando c�lculo pesado'); // Solo cuando el valor cambia
    return sum * 2;
  })
).subscribe(result => console.log('Resultado:', result));

setInterval(() => {
  a$.next(Math.random());
}, 100);

Problema 2: Monitoreo del uso de memoria

typescript
import { interval } from 'rxjs';
import { scan, tap } from 'rxjs';

let itemCount = 0;

interval(100).pipe(
  scan((acc, val) => {
    acc.push(val);
    itemCount = acc.length;
    return acc;
  }, [] as number[]),
  tap(() => {
    if (itemCount % 100 === 0) {
      console.log(`N�mero de elementos: ${itemCount}`);
      if (itemCount > 10000) {
        console.warn('� Uso de memoria demasiado alto');
      }
    }
  })
).subscribe();

Problema 3: Monitoreo del n�mero de suscripciones

typescript
import { Observable, Subject } from 'rxjs';

class MonitoredSubject<T> extends Subject<T> {
  private subscriptionCount = 0;

  subscribe(...args: any[]): any {
    this.subscriptionCount++;
    console.log(`N�mero de suscripciones: ${this.subscriptionCount}`);

    const subscription = super.subscribe(...args);

    const originalUnsubscribe = subscription.unsubscribe.bind(subscription);
    subscription.unsubscribe = () => {
      this.subscriptionCount--;
      console.log(`N�mero de suscripciones: ${this.subscriptionCount}`);
      originalUnsubscribe();
    };

    return subscription;
  }
}

// Uso
const data$ = new MonitoredSubject<number>();

const sub1 = data$.subscribe(v => console.log('Suscripci�n 1:', v));
const sub2 = data$.subscribe(v => console.log('Suscripci�n 2:', v));

sub1.unsubscribe();
sub2.unsubscribe();

// Salida:
// N�mero de suscripciones: 1
// N�mero de suscripciones: 2
// N�mero de suscripciones: 1
// N�mero de suscripciones: 0

Lista de verificaci�n de depuraci�n

Si surge un problema, verifica lo siguiente en orden.

markdown
## Verificaci�n b�sica
- [ ] �Est�s llamando a `subscribe()`?
- [ ] �`complete` o `error` no llegan demasiado pronto?
- [ ] �Los valores no est�n siendo excluidos por `filter` o `take`?
- [ ] �Est�s esperando la finalizaci�n del procesamiento as�ncrono?

## Verificaci�n de timing
- [ ] �Entiendes sincron�a/asincron�a?
- [ ] �Verificaste el impacto de `delay`, `debounceTime`, `throttleTime`?
- [ ] �Se cumple la condici�n de primera emisi�n de `combineLatest`?

## Verificaci�n de memoria
- [ ] �Est�s usando `unsubscribe` o `takeUntil`?
- [ ] �Configuraste `refCount: true` en `shareReplay`?
- [ ] �Est�s delimitando adecuadamente los Observables infinitos?

## Verificaci�n de rendimiento
- [ ] �No hay recalculaci�n excesiva? (considera `distinctUntilChanged`)
- [ ] �El n�mero de suscripciones no aumenta demasiado?
- [ ] �Asincronizan los procesos pesados con `observeOn(asyncScheduler)`?

Lista de verificaci�n de comprensi�n

Verifica si puedes responder a las siguientes preguntas.

markdown
## Depuraci�n b�sica
- [ ] Puedo depurar el flujo de valores usando tap
- [ ] Puedo identificar d�nde ocurren los errores
- [ ] Puedo verificar el timing de complete/error

## Uso de herramientas
- [ ] Conozco el uso b�sico de RxJS DevTools
- [ ] Puedo crear operadores de depuraci�n personalizados
- [ ] Puedo medir el timing usando timestamp

## Resoluci�n de problemas
- [ ] Puedo identificar la causa cuando no fluyen valores
- [ ] Puedo encontrar se�ales de fugas de memoria
- [ ] Puedo identificar problemas de rendimiento

## Prevenci�n
- [ ] Tengo el h�bito de depuraci�n gradual usando tap
- [ ] Implemento manejo de errores adecuadamente
- [ ] Conozco medidas contra fugas de memoria

Pr�ximos pasos

Una vez que entiendas las t�cnicas de depuraci�n, integra todo el conocimiento aprendido hasta ahora y aprende patrones pr�cticos.

Chapter 13: Colecci�n de patrones pr�cticos (en preparaci�n) - Colecci�n de patrones utilizables en el trabajo

P�ginas relacionadas

<� Ejercicios de pr�ctica

Problema 1: Identificar la causa cuando no fluyen valores

En el siguiente c�digo, identifica por qu� no se imprimen valores.

typescript
import { Subject, combineLatest } from 'rxjs';

const a$ = new Subject<number>();
const b$ = new Subject<number>();

combineLatest([a$, b$]).subscribe(([a, b]) => {
  console.log('Valores:', a, b);
});

a$.next(1);
console.log('Completado');
Respuesta

Causa

combineLatest no emite hasta que todos los streams emitan al menos un valor

Como b$ a�n no ha emitido un valor, solo a$.next(1) no provoca la emisi�n.

M�todo de correcci�n 1: Emitir valor tambi�n en b$

typescript
import { Subject, combineLatest } from 'rxjs';

const a$ = new Subject<number>();
const b$ = new Subject<number>();

combineLatest([a$, b$]).subscribe(([a, b]) => {
  console.log('Valores:', a, b);
});

a$.next(1);
b$.next(2); // � Aqu� se emite
console.log('Completado');

// Salida:
// Valores: 1 2
// Completado

M�todo de correcci�n 2: Usar BehaviorSubject

typescript
import { BehaviorSubject, combineLatest } from 'rxjs';

const a$ = new BehaviorSubject<number>(0); // Valor inicial
const b$ = new BehaviorSubject<number>(0);

combineLatest([a$, b$]).subscribe(([a, b]) => {
  console.log('Valores:', a, b);
});

// Salida: Valores: 0 0 (emite inmediatamente)

a$.next(1);
// Salida: Valores: 1 0

T�cnica de depuraci�n

Usando tap para verificar los valores de cada stream, puedes saber d�nde se detiene.

typescript
a$.pipe(tap(v => console.log('a$:', v)))
b$.pipe(tap(v => console.log('b$:', v)))

Problema 2: Corregir la fuga de memoria

El siguiente c�digo tiene una fuga de memoria. Corr�gelo.

typescript
import { interval } from 'rxjs';
import { Component } from '@angular/core';

class MyComponent implements Component {
  ngOnInit() {
    interval(1000).subscribe(n => {
      console.log('Temporizador:', n);
    });
  }

  ngOnDestroy() {
    console.log('Destruido');
  }
}
Ejemplo de respuesta

Problema

Al no hacer unsubscribe en ngOnDestroy, interval contin�a ejecut�ndose incluso despu�s de destruir el componente**

M�todo de correcci�n 1: Guardar Subscription y hacer unsubscribe

typescript
import { interval, Subscription } from 'rxjs';

class MyComponent {
  private subscription!: Subscription;

  ngOnInit() {
    this.subscription = interval(1000).subscribe(n => {
      console.log('Temporizador:', n);
    });
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
    console.log('Destruido y unsubscribe');
  }
}

M�todo de correcci�n 2: Usar takeUntil (recomendado)

typescript
import { interval, Subject } from 'rxjs';
import { takeUntil } from 'rxjs';

class MyComponent {
  private destroy$ = new Subject<void>();

  ngOnInit() {
    interval(1000).pipe(
      takeUntil(this.destroy$)
    ).subscribe(n => {
      console.log('Temporizador:', n);
    });
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    console.log('Destruido y unsubscribe');
  }
}

Puntos

  • Los Observables infinitos como interval requieren unsubscribe obligatoriamente
  • Se recomienda el patr�n takeUntil (permite gestionar m�ltiples suscripciones de forma unificada)
  • En Angular, si usas async pipe, unsubscribe se hace autom�ticamente

Problema 3: Problema de orden

En el siguiente c�digo, explica por qu� el orden no est� garantizado y corr�gelo.

typescript
import { from, of } from 'rxjs';
import { mergeMap, delay } from 'rxjs';

from([1, 2, 3]).pipe(
  mergeMap(x =>
    of(x).pipe(
      delay(Math.random() * 1000)
    )
  )
).subscribe(value => console.log(value));

// Ejemplo de salida: 2, 1, 3 (el orden no est� garantizado)
Respuesta

Problema

mergeMap ejecuta en paralelo, por lo que el orden de finalizaci�n depende del tiempo de ejecuci�n**

M�todo de correcci�n: Usar concatMap

typescript
import { from, of } from 'rxjs';
import { concatMap, delay, tap } from 'rxjs';

from([1, 2, 3]).pipe(
  tap(x => console.log('Inicio:', x)),
  concatMap(x =>
    of(x).pipe(
      delay(Math.random() * 1000),
      tap(v => console.log('Completado:', v))
    )
  )
).subscribe(value => console.log('Resultado:', value));

// Salida (siempre en este orden):
// Inicio: 1
// Completado: 1
// Resultado: 1
// Inicio: 2
// Completado: 2
// Resultado: 2
// Inicio: 3
// Completado: 3
// Resultado: 3

Raz�n

  • mergeMap: Ejecuci�n paralela, el orden de finalizaci�n no est� garantizado
  • concatMap: Ejecuci�n secuencial, siempre emite en el mismo orden que la entrada
  • switchMap: Solo el m�s reciente, procesos antiguos se cancelan
  • exhaustMap: Ignora nuevos procesos durante la ejecuci�n

Comparaci�n con diagramas de m�rmol

Entrada:  --1--2--3----|

mergeMap: --2--1--3--|  (orden de finalizaci�n)
concatMap: --1--2--3-| (orden de entrada)

Problema 4: Mejora de rendimiento

El siguiente c�digo recalcula frecuentemente. Mejora el rendimiento.

typescript
import { fromEvent } from 'rxjs';
import { map } from 'rxjs';

const input = document.querySelector('input')!;

fromEvent(input, 'input').pipe(
  map(e => (e.target as HTMLInputElement).value),
  map(value => {
    console.log('Ejecutando c�lculo pesado');
    return value.toUpperCase();
  })
).subscribe(result => console.log(result));

// Usuario escribe "hello"
// Ejecutando c�lculo pesado (h)
// Ejecutando c�lculo pesado (he)
// Ejecutando c�lculo pesado (hel)
// Ejecutando c�lculo pesado (hell)
// Ejecutando c�lculo pesado (hello)
Respuesta

M�todo de mejora 1: Esperar finalizaci�n de entrada con debounceTime

typescript
import { fromEvent } from 'rxjs';
import { map, debounceTime } from 'rxjs';

const input = document.querySelector('input')!;

fromEvent(input, 'input').pipe(
  debounceTime(300), // Ejecuta si no hay entrada durante 300ms
  map(e => (e.target as HTMLInputElement).value),
  map(value => {
    console.log('Ejecutando c�lculo pesado');
    return value.toUpperCase();
  })
).subscribe(result => console.log(result));

// Se ejecuta solo una vez despu�s de escribir "hello" y esperar 300ms

M�todo de mejora 2: Excluir duplicados con distinctUntilChanged

typescript
import { fromEvent } from 'rxjs';
import { map, debounceTime, distinctUntilChanged } from 'rxjs';

const input = document.querySelector('input')!;

fromEvent(input, 'input').pipe(
  debounceTime(300),
  map(e => (e.target as HTMLInputElement).value),
  distinctUntilChanged(), // Ignorar si es el mismo valor que la vez anterior
  map(value => {
    console.log('Ejecutando c�lculo pesado');
    return value.toUpperCase();
  })
).subscribe(result => console.log(result));

T�cnicas de mejora de rendimiento

  • debounceTime: Esperar finalizaci�n de entrada
  • throttleTime: Reducir a intervalos regulares
  • distinctUntilChanged: Excluir duplicados
  • observeOn(asyncScheduler): Asincronizar procesos pesados
  • shareReplay: Cachear resultados

Publicado bajo licencia CC-BY-4.0.