nodejs firebaseapp example array javascript-events stream iterator observable ecmascript-next

javascript events - firebaseapp - Eventos vs Streams vs Observables vs Asincrónicos Iterators



rxjs observable example (2)

Actualmente, la única forma estable de procesar una serie de resultados de sincronización en JavaScript es usar el sistema de eventos. Sin embargo, se están desarrollando tres alternativas:

Streams: https://streams.spec.whatwg.org
Observables: https://tc39.github.io/proposal-observable
Iteradores asincrónicos: https://tc39.github.io/proposal-async-iteration

¿Cuáles son las diferencias y los beneficios de cada uno sobre los eventos y los demás?

¿Alguno de estos tiene la intención de reemplazar eventos?


Mi entendimiento de Async Iterators es un poco limitado, pero por lo que entiendo WHATWG Streams es un caso especial de Async Iterators. Para obtener más información al respecto, consulte las preguntas frecuentes de la API de Streams . Aborda brevemente cómo difiere de Observables .

Tanto los Iteradores Async como los Observables son formas genéricas de manipular múltiples valores asíncronos. Por ahora no interoperan, pero parece que se está considerando la creación de Observables de los Iteradores Asincrónicos . Los observables por su naturaleza basada en empuje son mucho más parecidos al sistema de eventos actual, AsyncIterables está basado en extracción. Una vista simplificada sería:

------------------------------------------------------------------------- | | Singular | Plural | ------------------------------------------------------------------------- | Spatial (pull based) | Value | Iterable<Value> | ------------------------------------------------------------------------- | Temporal (push based) | Promise<Value> | Observable<Value> | ------------------------------------------------------------------------- | Temporal (pull based) | await on Promise | await on Iterable<Promise> | -------------------------------------------------------------------------

Representé AsyncIterables como Iterable<Promise> para hacer que la analogía sea más fácil de razonar. Tenga en cuenta que await Iterable<Promise> no es significativo, ya que debe usarse en a for await...of AsyncIterator loop.

Puedes encontrar una explicación más completa aquí .


Aquí hay aproximadamente dos categorías de API: pull and push.

Halar

Las API de extracción asíncrona son adecuadas para casos en los que se extraen datos de una fuente. Esta fuente puede ser un archivo, un conector de red, una lista de directorios o cualquier otra cosa. La clave es que el trabajo se hace para extraer o generar datos de la fuente cuando se le pregunte.

Los iteradores asíncronos son la primitiva de base aquí, que pretende ser una manifestación genérica del concepto de una fuente asíncrona basada en el arrastre. En tal fuente, usted:

  • Extraiga de un iterador asincrónico haciendo const promise = ai.next()
  • Espere el resultado usando const result = await promise .then() const result = await promise (o usando .then() )
  • Inspeccione el resultado para averiguar si es una excepción (arrojado), un valor intermedio ( { value, done: false }) , o una señal hecha ( { value: undefined, done: true } ).

Esto es similar a cómo los iteradores de sincronización son una manifestación genérica del concepto de una fuente de valor de sincronización basada en la extracción. Los pasos para un iterador de sincronización son exactamente los mismos que los de arriba, omitiendo el paso "esperar el resultado".

Los flujos legibles son un caso especial de iteradores asíncronos, destinados específicamente a encapsular fuentes de E / S como sockets / files / etc. Tienen API especializadas para canalizarlas a flujos modificables (que representan la otra mitad del ecosistema de E / S, sumideros) y manejar la contrapresión resultante. También se pueden especializar para manejar bytes de una manera eficiente "traiga su propio buffer". Todo esto recuerda algo a cómo los arreglos son un caso especial de iteradores sincronizados, optimizados para O (1) acceso indexado.

Otra característica de las API de extracción es que generalmente son de un solo consumidor. Quien saca el valor, ahora lo tiene, y no existe en la fuente async iterator / stream / etc. nunca más. Ha sido retirado por el consumidor.

En general, las API de extracción proporcionan una interfaz para comunicarse con alguna fuente de datos subyacente, lo que permite al consumidor expresar interés en ella. Esto está en contraste con ...

empujar

Las API Push son adecuadas para cuando algo genera datos, y los datos que se generan no se preocupan por si alguien quiere o no. Por ejemplo, no importa si alguien está interesado, sigue siendo cierto que su mouse se movió y luego hizo clic en algún lugar. Te gustaría manifestar esos hechos con una API de inserción. Luego, los consumidores -posiblemente varios de ellos- pueden suscribirse, para que se les envíen notificaciones sobre la ocurrencia de tales cosas.

A la API en sí misma no le importa si se suscriben cero, uno o muchos consumidores. Solo está manifestando un hecho sobre cosas que sucedieron en el universo.

Los eventos son una manifestación simple de esto. Puede suscribirse a EventTarget en el navegador, o EventEmitter en Node.js, y recibir notificaciones de los eventos que se envían. (Por lo general, pero no siempre, por el creador de EventTarget.)

Los observables son una versión más refinada de EventTarget. Su principal innovación es que la suscripción en sí está representada por un objeto de primera clase, el Observable, sobre el cual puede aplicar combinadores (como filtro, mapa, etc.). También toman la decisión de agrupar tres señales (denominadas convencionalmente siguiente, completo y error) en una, y otorgan a estas señales una semántica especial para que los combinators las respeten. Esto es opuesto a EventTarget, donde los nombres de los eventos no tienen semántica especial (a ningún método de EventTarget le importa si su evento se llama "completo" o "asdf"). EventEmitter in Node tiene alguna versión de este enfoque de semántica especial donde los eventos de "error" pueden bloquear el proceso, pero eso es bastante primitivo.

Otra buena característica de los observables sobre los eventos es que generalmente solo el creador de lo observable puede hacer que genere esas señales siguientes / error / completo. Mientras que en EventTarget, cualquiera puede llamar a dispatchEvent (). Esta separación de responsabilidades hace que el código sea mejor, en mi experiencia.

Pero al final, tanto los eventos como los observables son buenas API para impulsar las ocurrencias en el mundo, para los suscriptores que pueden sintonizar y desconectar en cualquier momento. Yo diría que los observables son la forma más moderna de hacerlo, y más agradable en algunos aspectos, pero los eventos están más extendidos y mejor entendidos. Entonces, si algo tenía la intención de reemplazar eventos, sería observable.

Empuje <-> tire

Vale la pena señalar que puedes construir cualquier acercamiento sobre el otro en caso de apuro:

  • Para construir un empuje encima de tirar, constantemente tirando de la API de extracción, y luego empuja los trozos a cualquier consumidor.
  • Para construir pull sobre push, suscríbase a la API push inmediatamente, cree un búfer que acumule todos los resultados, y cuando alguien tira, lo toma de ese búfer. (O espere hasta que el búfer se vuelva no vacío, si su consumidor está tirando más rápido de lo que empuja la API envuelta).

Este último generalmente es mucho más código para escribir que el primero.

Otro aspecto de tratar de adaptarse entre los dos es que solo las API de extracción pueden comunicar fácilmente la contrapresión. Puede agregar un canal lateral para enviar API para permitirles que comuniquen la contrapresión nuevamente a la fuente; Creo que Dart hace esto, y algunas personas intentan crear evoluciones de observables que tienen esta habilidad. Pero es IMO mucho más incómodo que simplemente elegir correctamente una API de extracción en primer lugar. La otra cara de esto es que si usa una API de inserción para exponer una fuente fundamentalmente basada en la extracción, no podrá comunicar la contrapresión. Este es el error cometido con las API WebSocket y XMLHttpRequest, por cierto.

En general, encuentro intentos de unificar todo en una API al envolver a otros equivocados. El método de empujar y tirar tiene áreas distintas, no muy superpuestas, donde cada una de ellas funciona bien, y decir que debemos elegir una de las cuatro API que mencionaste y aferrarnos a ella, como hacen algunas personas, es miope y genera un código incómodo.