En JavaScript, la asincronía permite que ciertas partes del código se ejecuten de forma independiente, para nosotros es como si se ejecutaran en paralelo. Hace un tiempo hice un Tutorial de asincronía donde puedes ver cómo se gestionaba antes mediante callbacks, y dos formas de gestionarlo ahora con promesas.
En este post vamos a ver otra forma de gestionar la asincronía. Esta vez mediante observables.
Qué es un Observable
Es un mecanismo parecido a los callbacks o las promesas. Permite gestionar peticiones de forma asíncrona y forma parte de la librería RXJS.
Con los observables vamos a usar el modelo de comunicación publicador/suscriptor. En este tipo de comunicación, tenemos un publicador y uno o varios suscriptores. El publicador decide cuándo se envía la información a los suscriptores, que la procesan en el momento de recibirla. El publicador lleva la voz cantante. Es quien manda.
Las promesas funcionan de este modo también, primero se hace la llamada a la promesa y en el bloque then() se indica qué haremos cuando termine su ejecución. Con los observables haremos algo similar, vamos a definir el comportamiento del publicador de datos y luego el del suscriptor. Algo parecido a esto:
import { Observable } from "rxjs" const observable = new Observable((subscriber) => { subscriber.next("Paso 1") subscriber.next("Paso 2") subscriber.next("Paso 3") }) observable.subscribe((data) => { console.log(`Se ha ejecutado el ${data}`) }) /* La salida por consola sería: Se ha ejecutado el Paso 1 Se ha ejecutado el Paso 2 Se ha ejecutado el Paso 3 */
De forma parecida a una función, los observables necesitan ser llamados. La diferencia principal con las funciones, es que los observables pueden devolver varios resultados. Estos resultados devueltos, pueden venir de forma síncrona o asíncrona, en otras palabras, pueden venir en un orden determinado o indeterminado.
Los observables pasan por diferentes fases: creación, suscripción, ejecución y destrucción. Lo verás en el ejemplo posterior.
Diferencia con las promesas
A diferencia de las promesas, los observables son de tipo lazy. Esto quiere decir que es necesario que nos suscribamos a un observable para obtener sus valores de retorno, mientras que una promesa se ejecuta en el mismo momento de ser llamada.
Los observables permiten devolver múltiples resultados, mientras que las promesas solo devuelven uno.
Además, una promesa no puede interrumpirse una vez es llamada. Un observable puede ser cancelado en cualquier momento.
Ejemplo de Observable
Imagina que necesitas reflejar en tu código las tareas que realiza un script (al que llamaremos worker) y quieres recibir notificaciones a medida que vaya completándolas. Podrías hacer algo así:
import { Observable } from "rxjs" const worker = new Observable((subscriber) => { // tareas del trabajador setTimeout(() => { subscriber.next("Tarea 1 completada") }, 2000) subscriber.next("Tarea 2 completada") subscriber.next("Tarea 3 completada") setTimeout(() => { subscriber.next("Tarea 4 completada") // ejecutamos el complete una vez que todas las tareas se han completado subscriber.complete() }, 2500) }) // nos suscribimos a las notificaciones del worker worker.subscribe({ next(notification) { console.log(notification) }, error(error) { console.log(`Ha ocurrido un error: ${error}`) }, complete() { console.log("Se han completado todas las tareas") }, })
El resultado sería este:
Tarea 2 completada Tarea 3 completada Tarea 1 completada Tarea 4 completada Se han completado todas las tareas
En este caso, el observable va devolviendo los resultados en un orden que depende de cuándo termina cada una de sus tareas. Ten en cuenta que, con este código, si la tarea 4 terminara de ejecutarse antes que la 1, esta última se cancelaría debido a la llamada del método complete() después de terminar la tarea 4.
Fíjate también que, en el suscriptor, hemos definido varias funciones para mostrar por pantalla las notificaciones, los errores y un mensaje final con los métodos next(), error() y complete().
Finalmente, comentar que si en el observable devuelves un error() las ejecuciones posteriores también se cancelan.
Conclusiones
Has podido ver qué son los observables y lo fácil que es trabajar con ellos. También has podido ver las diferencias principales con las promesas, lo cual puede darte una idea de cuándo puede ser conveniente usar un mecanismo de asincronía u otro. Por ejemplo, los observables pueden ser muy útiles cuando necesitas enviar notificaciones en una arquitectura orientada a eventos.