Proponen agregar Signals a JavaScript
(github.com/proposal-signals)- La propuesta de JavaScript Signals de TC39 es una dirección inicial para estandarizar primitivas reactivas que permitan rastrear de forma eficiente el estado de la UI y el estado calculado, y actualmente es un borrador en nivel Stage 1
- La propuesta se enfoca más en la semántica central del grafo de Signals y en los mecanismos de rastreo automático que los frameworks pueden compartir, que en una API superficial para uso directo de desarrolladores de aplicaciones
Signal.State,Signal.ComputedySignal.subtle.Watcherson las API clave, y los cálculos apuntan a lazy evaluation, caché, rastreo automático de dependencias y ejecución libre de glitches- El objetivo de los Signals integrados es aumentar la interoperabilidad entre varios frameworks como Angular, Ember, MobX, Preact, Qwik, Solid, Svelte y Vue, además de abrir posibilidades de soporte para depuración y análisis de rendimiento en DevTools
- El grupo de la propuesta planea pasar por varios polyfills de nivel producción, integraciones con frameworks, validación en aplicaciones grandes y benchmarks de rendimiento antes de Stage 2, y la estandarización podría tardar al menos 2 a 3 años o más
La posición actual de la propuesta JavaScript Signals
- JavaScript Signals se presenta como una propuesta Stage 1 dentro del proceso de TC39
- El documento actual es un intento de alinear una dirección común para el ecosistema de JavaScript, parecido a lo que fueron Promises/A+ antes de la estandarización de
Promiseen ES2015 - Hay un polyfill disponible para probarlo directamente
- Los champions de la propuesta y autores originales armaron el borrador actual con base en aportes de diseño de varios frameworks y librerías
El problema al que apunta la estandarización
- Las interfaces complejas tienen que almacenar valores, calcularlos, invalidarlos, sincronizarlos y empujarlos hacia la capa de vista, y Signals busca ofrecer una infraestructura de manejo de estado para ese trabajo repetitivo
- En un ejemplo de Vanilla JS,
counter,isEven,parityyrenderquedan directamente entrelazados y aparecen los siguientes problemas- El estado y el sistema de renderizado quedan fuertemente acoplados
- Aunque
parityno cambie, como cuandocounterpasa de 2 a 4, ocurren cálculos y renderizados innecesarios - Si otras partes de la UI quieren suscribirse solo a
counter,isEvenoparity, la gestión manual de suscripciones y cancelaciones se vuelve compleja - Si se agrega pub/sub en varias capas, aumentan el boilerplate y el bookkeeping de suscripciones, además del riesgo de fugas de memoria
- Un ejemplo basado en Signals usa
Signal.StateySignal.Computedpara tratar valores, cálculos y efectos secundarios de una sola manera- No se necesitan suscripciones manuales
- Los Signals calculados detectan automáticamente de qué Signals dependen
- Los cálculos solo se ejecutan cuando alguien pide explícitamente el valor
- Los Signals calculados guardan en caché el último valor
API principal de la propuesta
Signal<T>se define como un valor legible que tieneget(): TSignal.State<T>es un Signal escribible- El constructor recibe un valor inicial y opciones
- Se lee con
get()y se modifica conset(t)
Signal.Computed<T>es un Signal calculado basado en otros Signals- Se calcula a partir del valor que devuelve un callback
- Rastrea dependencias automáticamente
- Su valor se calcula de forma lazy y queda en caché
Signal.subtlecontiene API avanzadas, más cercanas a autores de frameworks o implementaciones de DevToolsuntrack(cb)permite leer un Signal sin rastreocurrentComputed()devuelve el computed Signal que se está rastreando en ese momentointrospectSources,introspectSinks,hasSinksyhasSourcesson API para observar el grafoWatcheres la base para implementar efectos a nivel framework y scheduling al detectar cambios en Signals
SignalOptions<T>soporta una función de comparación personalizadaequalsy hookswatched/unwatched
Funcionamiento y modelo de ejecución
- Un Signal representa una celda de datos que puede cambiar con el tiempo, y se divide en
stateocomputed - Un Signal calculado registra automáticamente los Signals que leyó durante su ejecución, y luego cuando vuelve a leerse verifica si cambiaron sus dependencias anteriores
- Los cálculos son pull-based
- Aunque cambien las dependencias, no se recalculan de inmediato
- Se recalculan cuando hace falta y alguien los lee con
.get()
- Las escrituras en State Signals se reflejan de manera síncrona
- Después de
.set(), si se lee un computed Signal que depende de ese valor, se recalcula de inmediato si hace falta - No hay batching integrado
- Después de
- El callback
notifyde un Watcher puede ejecutarse de forma síncrona durante.set()- Pero durante
notifyno se puede leer ni escribir Signals - El trabajo real de lectura o escritura debe programarse después
- Pero durante
- Si el callback de un computed Signal lanza una excepción, esa excepción también se guarda en caché como si fuera un valor, y se vuelve a lanzar al releer el Signal
Motivos para la estandarización
- Las implementaciones de Signals de cada framework tienen su propio mecanismo de rastreo automático, lo que dificulta compartir modelos, componentes y librerías entre frameworks
- El objetivo de la propuesta es separar el modelo reactivo de la vista de renderizado
- Para que los desarrolladores no tengan que reescribir el código no relacionado con UI al cambiar de tecnología de renderizado
- Para permitir crear en JavaScript modelos reactivos que puedan compartirse entre varios contextos
- En rendimiento y memoria, se indica que una implementación integrada podría ser más eficiente que una en JS por un factor constante menor, pero que el motor no cambiará mágicamente el algoritmo
- Desde el lado de DevTools, Signals integrados podrían mostrar mejor la siguiente información
- El call stack de cadenas de computed Signals
- El grafo de referencias entre Signals
- Las relaciones de dependencia necesarias para depurar el uso de memoria
- Si llegan a formar parte de la librería estándar, también se esperan efectos secundarios como reducción del tamaño del bundle, mejor estabilidad y calidad, y una terminología común entre proyectos
Objetivos y restricciones de diseño
- Las funciones centrales incluyen Signals escribibles, Signals calculados, reacción al estado dirty, scheduling propio del framework,
untracky composición entre varias bases de código - Los computed Signals apuntan a ser glitch-free
- Para evitar cálculos innecesarios, ejecutan las partes potencialmente dirty del grafo en orden topológico
- Buscan eliminar cálculos duplicados
- No se incluye un scheduling forzado integrado al estilo Promise, para que los frameworks puedan manejar su propio scheduling
- Para evitar el mal uso de callbacks de reacción síncrona, dentro de
notifyde Watcher está prohibido leer y escribir Signals untrackse considera una vía de escape insegura- Si un Signal leído sin rastreo influye en el resultado del cálculo, el computed Signal podría no actualizarse cuando ese Signal cambie
- La API prioriza servir como base para implementaciones de frameworks, y no está diseñada especialmente para ser ergonómica para desarrolladores de aplicaciones en general
effect y Watcher
- La propuesta no incluye una función integrada como
effect() - El scheduling de effects está entrelazado con el ciclo de render del framework, disposal y manejo de ownership, así que la API estándar de JavaScript no intenta resolverlo directamente
- En cambio,
Signal.subtle.Watcherofrece una base de bajo nivel para implementar effectsnotifyse llama cuando cambian las dependencias de los Signals observadosgetPending()permite ver qué Signals siguen dirty- Los effects que necesitan cleanup deben limpiarse con
unwatch
- Los Signals que un Watcher está observando pueden seguir vivos mientras su estado interno siga siendo alcanzable, por lo que al limpiar un effect hace falta llamar a
Watcher.prototype.unwatch
Funciones que faltan en el borrador actual
- Async no está incluido en el modelo actual
- Signals siempre se tratan como valores evaluables de forma síncrona
- Hay cierta posibilidad de modelar el estado de loading como excepción, y la discusión de mejoras está en Issue #30
- Transactions tampoco está incluido
- Para mantener al mismo tiempo un estado “from” y un estado “to” en una transición de pantalla, aparece el problema de hacer fork del estado del grafo de Signals
- La discusión relacionada está en Issue #73
- Algunos convenience methods también están fuera del borrador actual
- Estas funciones quedaron fuera por falta de consenso entre frameworks y porque pueden resolverse en capas superiores, aunque podrían revisarse de nuevo después del prototipo
Plan de desarrollo y calendario de estandarización
- Esta propuesta estuvo en la agenda de TC39 Stage 1 en abril de 2024, y el documento explica que por ahora también puede verse como si estuviera en Stage 0
- Antes de proponer Stage 2, planean el siguiente trabajo
- Desarrollar varios polyfills de nivel producción
- Probar en distintos frameworks y pasar tests al estilo test262
- Verificar rendimiento con un conjunto exhaustivo de benchmarks de signals/frameworks
- Integrar la API propuesta en varios frameworks representativos de JS y en algunas aplicaciones grandes
- Entender la capacidad de extensión de la API y decidir qué incluir
- El grupo de la propuesta quiere avanzar con cautela para evitar que una forma incorrecta de Signals se estandarice demasiado pronto
- El FAQ estima que pasarán al menos 2 a 3 años antes de que los Signals estándar puedan usarse en todos los navegadores sin polyfill
- El polyfill actual puede usarse, pero se advierte que la API podría cambiar durante la revisión, por lo que conviene no depender de su estabilidad
Modelo de uso resumido en el FAQ
- Los Signals integrados son independientes de la tecnología de renderizado
- El documento explica que son posibles enfoques como Preact con VDOM, Solid con DOM nativo o Vue con enfoque mixto
- Para desarrolladores de aplicaciones, normalmente tiene más sentido usar Signals a través de un framework
- El framework administra Watcher,
untrack, ownership, disposal y el scheduling del render de DOM
- El framework administra Watcher,
- Puede usarse junto con SSR, hydration y resumability
- Qwik ya usa Signals con estas propiedades, y quienes impulsan la propuesta consideran que los Signals reanudables de Qwik pueden modelarse como una combinación de State y Computed
- Signals y
Proxyson complementarios- Proxy intercepta operaciones sobre objetos superficiales, y Signals coordina el grafo de dependencias de las celdas de datos
- Poner Signals detrás de Proxy puede hacer más ergonómicas las estructuras reactivas anidadas
- Signals no son streams, sino celdas que representan el valor actual
- Si se escribe dos veces seguidas en un State Signal y no pasa nada más, la primera escritura podría no ser visible para un computed Signal o un effect
- El documento considera esto como una característica opuesta al comportamiento de los streams, y dice que para streams son más adecuadas otras construcciones como async iterable u observable
1 comentarios
Opiniones de Hacker News
¿Soy el único al que el ejemplo en JavaScript puro le parece más fácil de leer y manejar?
Dicen que “la configuración tiene mucho ruido y boilerplate”, pero el ejemplo con signals se ve igual de ruidoso y lleno de boilerplate, y además agrega un concepto nuevo difícil de entender para principiantes.
Eso de que “si counter cambia de 2 a 4, parity no cambia, pero se hacen cálculos y renderizados innecesarios” suena a memoización prematura.
Si otra parte de la UI quiere renderizarse cuando se actualiza counter, es cierto que ese ejemplo strawman no es adecuado; en ese caso se pueden usar otros enfoques como signals, manejo de eventos o un almacén central de estado (tipo Redux).
Si otra parte de la UI depende solo de isEven o parity, y eso es parte de la estructura central de la app, quizá se podría cambiar el enfoque, pero en la mayoría de los casos no es así. Que “una función render que solo depende de parity tenga que saber que debe suscribirse a counter” tampoco es necesariamente una carga injusta, y las funciones de cálculo puras tienen la ventaja de que es fácil identificar sus entradas.
Intentar estandarizar signals, un concepto cada vez más usado en desarrollo de UI, es algo digno de elogio. Dejando de lado discusiones de detalle como cuánto boilerplate hay o si hay que construir un sistema de eventos propio, si varios frameworks usan signals puede haber una razón, y vale la pena intentar estandarizarlos aunque tome tiempo.
https://preactjs.com/guide/v10/signals
En Preact, cuando un signal baja por el árbol como props o context, solo se pasa la referencia al signal, y como los componentes observan el signal y no el valor, actualizar el signal puede no volver a renderizar el componente. En la práctica, se puede ir directamente al componente del árbol que accede a
.value.Además, un signal rastrea cuándo se accede a su valor y cuándo se actualiza; en Preact, si dentro de un componente se accede a
.valuede un signal, el componente se vuelve a renderizar automáticamente cuando cambia el valor de ese signal.Por experiencia, una gran ventaja es que permite modularizar el estado reactivo. En un estilo imperativo se necesita estado adicional para rastrear cambios, y la modularidad se logra mediante abstracciones. Hay que usarlo solo cuando hace falta.
Crear un ejemplo simple pero aplicable es una cuestión de equilibrio. Los casos donde la reactividad es claramente beneficiosa suelen ser más complejos, así que son más difíciles de mostrar que un ejemplo simple pero menos aplicable.
Cuando se agregaron Promises a JavaScript, me generaba rechazo pensar que habría que usar
new Promisepor todas partes.En la práctica, puedo contar con los dedos de las dos manos las veces que escribí
new Promisedirectamente. En cambio, terminé usando.thenmucho más seguido, especialmente al trabajar con librerías de terceros.Al final, el efecto cotidiano de que Promise se agregara a JavaScript fue ofrecer una interfaz bastante simple, en general robusta y casi universal para las distintas conductas y funcionalidades especiales que ofrecen las librerías de terceros. Ya sea leer un archivo, hacer una solicitud a una API o producir una salida en una etapa de build, al usar
.then(res => …)uno siente que ya está a medio camino de algo que funciona.Si esta propuesta de Signal cumple un rol parecido en medio de la explosión cámbrica de frameworks de UI reactivos, estoy a favor. Incluso podría ayudar a que la reactividad se extienda fuera de la UI. A menudo he imaginado árboles de estado de recálculo incremental para cosas que no son estado de UI.
new Promisedirectamente.El
.thende las Promises iniciales era una gran mejora frente a callbacks anidados y está bien para cadenas simples, pero cuando hay que encadenar distintas Promises de forma condicional, manejar errores de manera diferente en cada cadena o hacer retornos tempranos, el código puede volverse mucho más difícil de leer y manejar.Con async/await se pueden escribir las llamadas como si no fueran Promises, poner fácilmente try/catch alrededor de una llamada específica a Promise y hacer retornos tempranos de forma natural.
No entiendo por qué esto debería convertirse en parte del lenguaje. Se puede hacer con bibliotecas, y ya existen bibliotecas así. Como es pequeño, incluirlo en el código no supone una gran carga, y agregar cosas al lenguaje no debería ser un objetivo en sí mismo.
Es arrogante pensar que, como las bibliotecas de UI actuales de JS diseñaron tan bien los signals, deberían formar parte del lenguaje. Hay muchas implementaciones de signals con distintos compromisos, y ninguna de ellas merece un lugar especial en la especificación de JavaScript.
Antes de usar signals, estas bibliotecas usaban DOM virtual. Por suerte, el DOM virtual no pasó a formar parte de JS; ¿qué tienen de distinto los signals? Nada. El argumento para estandarizarlos es incluso más débil que en el caso del DOM virtual.
¿Vamos a apilar todo lo que esté de moda en un runtime donde prácticamente no hay forma de quitar funciones que ya no queremos sin romper la web? Es bastante cortoplacista.
La UI reactiva ganó. Incluso en aplicaciones pequeñas, al manejar estado la complejidad se dispara, y eso es lo principal que hace difícil usar JS puro. Para mí, cualquier framework reactivo es mejor que JS puro, y si eso es así, quizá falte algún componente.
Ya pasaron unos 10 años, así que es momento de pensar en qué límite podría estandarizarse. Como con Promise, si se hace bien, podría reducir la complejidad de casos de uso muy comunes.
Una mejor evaluación sería preguntar: “¿los frameworks reactivos existentes usarían esta propuesta?”. Si no, habría que ver por qué, qué falta, qué sobra y qué podemos aprender de la UI y la reactividad en otros lenguajes. Vale la pena depurar esa experiencia dispersa.
Esa necesidad se puede debatir, pero si se va a ampliar la biblioteca estándar, mirar lo popular me parece un buen enfoque.
Los signals no son un reemplazo del DOM virtual.
Cuando necesitas señalar algo a toda la aplicación, usas eventos.
window.dispatchEvent(new Event('counterChange'));Y cualquier parte de la aplicación que quiera reaccionar puede suscribirse así:
window.addEventListener('counterChange', () => { ... do something ... });¿Qué tiene de malo este enfoque?
El manejo de eventos se vuelve desordenado con mucha facilidad. Para verlo más a fondo, mira el bubbling y la propagación de eventos.
Las aplicaciones grandes necesitan un manejo de eventos robusto, y esa es una ventaja de frameworks como Angular y Vue que hoy no siempre se nota.
No vas a querer usar tal cual la API estándar de manejo de eventos sin un framework. Cuando tienes que manejar altas, bajas, duplicación, disparos, eliminación, disparos de una sola vez, etc., para muchos elementos, pueden aparecer efectos secundarios no deseados bastante graves.
La diferencia con signals es que el valor resultante solo se calcula cuando el consumidor final lee el valor. Se separa el momento en que se escribe realmente en el signal de la programación asíncrona de la actualización de render, y la cadena de cálculos que realiza el observador se ejecuta solo una vez durante el render.
Los valores intermedios enviados mediante el signal desaparecen, así que es difícil hacer muchas cosas interesantes dentro de ellos; en la práctica, se parecen más a una capa de abstracción avanzada para coordinar el ciclo de renderizado.
También pueden tener mejor rendimiento. Por ejemplo, si hay un cálculo que depende de dos valores,
result = a ? b : 0, cuando a es falso no hace falta recalcular aunque b cambie. Con signals esto ocurre automáticamente, pero con publicación/suscripción tradicional hace falta bastante código.También es difícil garantizar que todos los listeners no creen ese tipo de disparos en cadena.
Durante décadas he intentado entender por qué a la gente le resulta tan difícil el seguimiento del estado y las actualizaciones del DOM.
Claro que hace falta un poco de disciplina, pero me parece mucho más simple que las soluciones que aparecen cada pocos años. Backbone, Knockout, Angular, React, modificar el propio lenguaje, etc.; quizá mi forma de pensar sea fundamentalmente distinta.
Incluso se nota en el nombre de las funciones. A actualizar
innerTextle llaman “render”, pero en realidad no estás renderizando. En el mejor de los casos, el navegador es el que renderiza, y lo mismo aplica a todo lo demás relacionado con el pintado. Me resulta realmente desconcertante, como un intento desesperado de complicar una de las funciones más simples del DOM.Cuando se vuelven más complejas, ya no lo es.
El progreso en la programación puede verse como el proceso de eliminar los rituales y la disciplina estricta necesarios para obtener buenos resultados.
No digo que React sea la siguiente evolución, pero signals claramente son un paso en la dirección correcta.
Sincronizar el DOM con el estado de los datos no es demasiado difícil en sí, pero hacerlo con muy buen rendimiento a 60fps es tremendamente difícil. Sobre todo cuando también quieres crear una API que no tenga fugas y que no sea demasiado engorrosa.
Sinceramente, puede ser más fácil dibujar píxeles en un canvas como en un juego que transformar cambios y reflejarlos en un árbol DOM vivo.
Promises son un buen caso de éxito, pero si no hubiera existido async/await, no habría sido estrictamente necesario estandarizarlas.
El borrador actual dice estar basado en diseños de autores/mantenedores de Angular, Bubble, Ember, FAST, MobX, Preact, Qwik, RxJS, Solid, Starbeam, Svelte, Vue, Wiz, etc., y me da curiosidad saber cómo ven esta propuesta los autores de las bibliotecas existentes. También es interesante que React no esté en la lista.
Signals se parecen un poco a los canales, pero se diferencian en que son de difusión en vez de tener un único receptor. Sería genial si se pudiera aprovechar esto para que los web workers se comuniquen mediante canales en lugar de callbacks
onMessage. Especialmente si, como en Go, se pudiera hacerselectsobre signals/channels/promises; eso tendría ventajas sintácticas frente a manejar varios mecanismos de mensajería concurrente con callbacks. Por ejemplo, permitir incluir signals enPromise.any.x instanceof Promisesimplemente no funciona. Si el métodothende mi biblioteca recibe un callback de catch y el de otra biblioteca no, silenciosamente no interoperan y no hay forma de detectarlo. ¿Cuándo se ejecutafinally? ¿Qué expectativas puedes tener sobre qué tan asincrónicamente se ejecutarán los callbacks?Sin un estándar, toda biblioteca que use Promise tendría que traer su propio polyfill, porque no puede confiar en lo que ya existe. Y tampoco puede consumir realmente Promises de otras bibliotecas, porque no puede confiar en que se comporten de la forma esperada.
No es una conjetura: así fue en la práctica durante años, y fue un infierno que mucha gente tuvo que soportar.
Mi intuición vaga es que signals se parecen demasiado a un
useEffect()generalizado, y que si entraran en React harían más confuso qué ocurre durante el ciclo de renderizado. Para bien o para mal, React eligió un enfoque de actualización distinto al de signals. Aunque podría estar equivocado sobre su aplicabilidad.useEffectaísla limpiamente el comportamiento imperativo.Esto se parece mucho al data binding de Ember, y al final puede convertirse en una pesadilla imperativa. El estado por defecto se parece más a “un arma apuntándote al pie”, y evitar que termine así requiere una enorme carga cognitiva y metapatrones.
https://nodejs.org/en/learn/asynchronous-work/the-nodejs-eve...
No entendí el ejemplo del README enlazado
// A library or framework defines effects based on other Signal primitivesdeclare function effect(cb: () => void): (() => void);¿Qué biblioteca? ¿Qué framework? Ahí me perdí. ¿Qué es
effect?effect(() => element.innerText = parity.get());¿Cómo sabe
effectque debe llamar a esta lambda cada vez que cambia parity? ¿Llama a esta lambda cada vez que cambia un signal? Si es así, ¿por qué se habla de caché? Probablemente no sea esoEn todo caso, si entendí bien lo que los autores querían transmitir, la idea de los signals en sí parece válida. Pero el gran problema de esta arquitectura desacoplada es que, cuando la aplicación se vuelve lo suficientemente compleja, uno se pierde tratando de rastrear por qué ocurre cierto evento. Idealmente, los signals deberían modificar el stack trace para que, cuando se llame un callback, ya incluya el stack trace del código que disparó originalmente el signal
effect, que permite ejecutar código arbitrario en reacción a actualizaciones de signals. La introducción a signals y effects en la documentación de Preact es buena: https://preactjs.com/guide/v10/signals#effectfnSegún entiendo, este tipo de función effect primero ejecuta el callback una vez para ver a qué signals se accedió durante la ejecución, y vuelve a llamar el callback cada vez que se actualiza un signal del que depende. Si el acceso al signal es sincrónico y de un solo hilo, el simple hecho de acceder a un signal durante la ejecución del callback permite saber que ese callback debe suscribirse a ese signal
También se puede hacer con getters. La función effect rastrea a qué propiedad de un signal se accedió desde el método getter, y tengo entendido que Vue 2 usaba este enfoque antes. También se puede rastrear el acceso a objetos con proxies. El ejemplo de la propuesta tiene un método
getque se llama para acceder al valor del signal, y mediante la ejecución de ese método se pueden rastrear las dependencias[1] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
[2] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
parity.get()registra una dependencia para la función pasada aeffect(). Cuando se actualizaparity, se llama a esa funciónNo se llama cada vez que cambia un signal, sino solo cuando cambia un signal del que depende
En este caso,
paritydepende deisEven, eisEvendepende decounter. Así que, cuandocounterse actualiza, se invalida toda la cadena de dependencias y, al invalidarseparity, el callback se ejecuta de nuevoeffecthipotético, una lectura crea una arista entre el nodo de estado del signal y el nodo de cálculo del effect, haciendo en la práctica que este último se suscriba a escrituras posteriores del primero para decidir cuándo volver a ejecutar el cálculoeffectes una función arbitraria que uno quiere llamarEn signals, el mecanismo de seguimiento de dependencias sabe qué valores deben recalcularse y, como resultado, el sistema también sabe qué funciones debe volver a llamar
effectRelacionado con esto, existe S.js: https://github.com/adamhaile/s
Me gustan los signals. Al crear UI los prefiero por encima de casi cualquier otro primitivo, quizá con la única excepción del algoritmo de restricciones cassowary. Intento imitar signals en todos los lenguajes que uso por diversión
Pero no creo en absoluto que sea algo que deba entrar en el lenguaje JavaScript en sí. Ojalá dejaran tranquilo al lenguaje por un tiempo. A la gente ya le cuesta seguirle el ritmo, y TC-39 ya está haciendo que la gente se asuste del lenguaje y se aleje
Esto se parece mucho a MobX, mi sistema de effects favorito en JS
La versión de MobX sería así
import { observable, computed, autorun } from 'mobx';const counter = observable.box(0);const isEven = computed(() => (counter.get() & 1) === 0);const parity = computed(() => isEven.get() ? "even" : "odd");autorun(() => { element.innerText = parity.get(); });setInterval(() => counter.set(counter.get() + 1), 1000);Se siente como “¡metamos en la biblioteca estándar el framework que uso últimamente!”
Es parecido a tatuarse el nombre de tu novia en el cuerpo
Es poner en la biblioteca estándar un componente básico hacia el que la mayoría de los frameworks han convergido para usar
Las Promises también entraron a la biblioteca estándar después de usarse ampliamente, y esto es parecido