Subject y BehaviorSubject: dos hermanos acróbatas en el mundo de la programación reactiva
\n\nSi alguna vez has mirado en Stack Overflow la discusión «What is the difference between Subject and BehaviorSubject?», sabes que este tema acumula miles de visitas. Y no es de extrañar: es una de esas preguntas que, tarde o temprano, todo desarrollador que trabaja con RxJS en TypeScript se plantea.
\n\nImagina que gestionas una transmisión en vivo de datos. Subject es como un micrófono abierto: el que se suscribe primero recibe todo lo que se diga después. BehaviorSubject, en cambio, es un micrófono que siempre guarda la última frase y se la repite inmediatamente a un nuevo oyente. ¿Notas la diferencia?
\n\nEn este artículo no solo repetiremos la documentación. Analizaremos casos reales, mostraremos ejemplos de código en TypeScript y daremos reglas claras para elegir. ¡Allá vamos!
\n\n¿Qué es Subject en TypeScript?
\n\nSubject es un tipo especial de Observable que también actúa como Observer. Puede recibir datos a través del método next() y distribuirlos a todos los suscriptores. Pero hay un detalle importante: Subject no almacena valores anteriores.
Un ejemplo simple en TypeScript:
\n\nimport { Subject } from 'rxjs';\n\nconst subject = new Subject<string>();\n\n// Nos suscribimos después de que los datos ya se han enviado\nsubject.next('Primer mensaje');\nsubject.next('Segundo mensaje');\n\n// Este suscriptor no recibirá nada\nsubject.subscribe({\n next: (value) => console.log('Suscriptor:', value)\n});\n\n// Esto sí se verá\nsubject.next('Tercer mensaje');\n\n\n¿Qué se mostrará en la consola? Solo «Suscriptor: Tercer mensaje». Los dos primeros mensajes se pierden para siempre para este suscriptor. Este efecto se llama «arranque en frío»: solo recibes los datos que se enviaron después de la suscripción.
\n\nBehaviorSubject: cuando necesitas recordar el pasado
\n\nBehaviorSubject es como Subject, pero con una supercapacidad: siempre almacena el último valor y lo entrega al nuevo suscriptor inmediatamente al suscribirse. Una condición obligatoria es que debes especificar un valor inicial.
\n\nimport { BehaviorSubject } from 'rxjs';\n\nconst behaviorSubject = new BehaviorSubject<string>('Valor inicial');\n\n// Nos suscribimos — recibimos inmediatamente el último valor\nbehaviorSubject.subscribe({\n next: (value) => console.log('Primer suscriptor:', value)\n});\n\nbehaviorSubject.next('Primera actualización');\nbehaviorSubject.next('Segunda actualización');\n\n// Este suscriptor recibirá 'Segunda actualización' de inmediato\nbehaviorSubject.subscribe({\n next: (value) => console.log('Segundo suscriptor:', value)\n});\n\n\nSalida en la consola:
\nPrimer suscriptor: Valor inicial\nPrimer suscriptor: Primera actualización\nPrimer suscriptor: Segunda actualización\nSegundo suscriptor: Segunda actualización\n\n\n¿Ves? El segundo suscriptor recibió inmediatamente el último valor, aunque se suscribió después. Esta es la diferencia clave entre BehaviorSubject y Subject.
\n\nComparación de Subject y BehaviorSubject: tabla de diferencias
\n\nResumamos todas las diferencias en una tabla clara que te será útil en una entrevista o en un proyecto real.
\n\n- \n
- Valor inicial: Subject — no. BehaviorSubject — obligatorio. \n
- Almacenamiento del último valor: Subject — no almacena. BehaviorSubject — almacena y lo entrega al nuevo suscriptor. \n
- Comportamiento con suscripción tardía: Subject — omite todos los eventos anteriores. BehaviorSubject — entrega el último valor. \n
- Uso con Angular: Subject — para eventos (clics, temporizadores). BehaviorSubject — para estado (datos de usuario, configuraciones). \n
- Método getValue(): Subject — no existe. BehaviorSubject — sí, devuelve el valor actual de forma síncrona. \n