Una fuente, dos lectores independientes

Empate

Requisito

Una fuente de lecturas debe alimentar dos cómputos independientes — el total y el pico — ejecutándose exactamente una vez. La fuente incrementa un contador cada vez que corre; imprime el total, el pico y el contador para demostrar la pasada única. Los datos están en el código; las dos versiones deben imprimir las líneas que aparecen bajo Salida esperada.

Salida esperada
total: 74
peak: 25
source runs: 1

Lado a lado

RxDart

FxDart

Por qué difieren

Ambos modelos chocan aquí con el mismo muro: sus fuentes se reinician por consumidor. Escuchar dos veces un stream llano de suscripción única es un error; iterar dos veces un generador sync* lo ejecuta dos veces en silencio. Y ambas bibliotecas responden con la misma idea — compartir una sola pasada. RxDart hace el stream conectable: publish() difiere la fuente, ambas reducciones se suscriben, y connect() arranca la única suscripción que las alimenta. El fork de FxDart ramifica una sola iteración con buffer: cada fork del mismo objeto iterable es un cursor independiente sobre un buffer compartido, así que el cuerpo del generador corre una vez sin importar cuántos lectores tiren de él.

Las diferencias son textura, no capacidad. La versión rx es sensible al orden — los lectores deben engancharse antes de connect(), y un rezagado se pierde eventos; eso es push: la entrega ocurre estés listo o no. La versión fx es sensible a la identidad — debes hacer fork del mismo objeto, y un fork que se rezaga simplemente reproduce el buffer a su propio ritmo; eso es pull: los valores esperan a la demanda. El coste es memoria: el buffer compartido retiene cada valor hasta que el fork más lento lo haya consumido, así que un lector muy rezagado mantiene viva la pasada entera, donde el rezagado de rx simplemente se la habría perdido. Cualquiera de las dos es una buena respuesta a «hacer un tee del pipeline», lo que convierte esto en un empate — elige la que encaje con el modelo en el que ya vive el resto de tu código.

Benchmark

Apple M1 Max, 32 GB de RAM · Dart 3.12.2 (compilado AOT) · 2026-08-18

N = 100

Tiempo Empate

RxDart 20 µs
FxDart 2.3 µs

Memoria pico Empate

RxDart 16.7 MB
FxDart 16.3 MB

N = 1,000,000

Tiempo Gana FxDart

RxDart 139.4 ms
FxDart 17.3 ms

Memoria pico Gana FxDart

RxDart 24.3 MB
FxDart 21.5 MB

Las barras son medianas de iteraciones cronometradas repetidas en procesos nuevos por lado (los N pequeños se agrupan por resolución del temporizador). Dos lados a menos del 5% entre sí — o a menos de 0.6 ms, una diferencia que nadie puede percibir — cuentan como empate; las carreras relativas ajustadas se vuelven a medir hasta 5 veces. En una app, cualquier cosa por debajo de unos pocos milisegundos es invisible para el usuario, gane la barra que gane. La memoria es el RSS pico del proceso. La VM de Dart y el dataset son idénticos en ambos lados, así que la diferencia entre las dos barras es lo que retiene el pipeline en sí.