Rendirse tras tres fallos
Requisito
Un feed de diez sondas de salud corre en orden; las sondas 2, 5, 7, 8 y 9 lanzan. Detén la ejecución en el momento en que se vea el tercer fallo (incluyéndolo), y luego imprime tres conteos: processed — sondas que entraron al pipeline antes del corte; failures — cuántas de esas lanzaron; y probes run — cuerpos de sonda realmente ejecutados, contabilizados por un contador de efecto colateral dentro de la propia sonda. Las sondas posteriores no deben ejecutarse jamás, así que el conteo de ejecuciones tiene que coincidir con el de procesadas. El calendario está en el código; las dos versiones deben imprimir las líneas que aparecen bajo Salida esperada.
Salida esperada
processed: 7 failures: 3 probes run: 7
Lado a lado
RxDart
FxDart
Por qué difieren
El núcleo de conteo es el mismo en ambos lados — scan
pliega un estado acumulado (done, fails), y un operador
take-inclusivo corta el pipeline en el tercer fallo
(takeUntilInclusive(fails == 3) en un lado,
takeWhileInclusive(fails < 3) en el otro). Ambos
además detienen el trabajo de verdad: probes run: 7
demuestra que cancelar la suscripción y dejar de tirar son frenos
igual de efectivos.
La diferencia es lo que cada lado tuvo que hacer antes de
que scan pudiera contar. Una sonda que lanza vive en el canal de
errores del stream, donde scan no puede verla — y donde terminaría el
stream en el fallo número uno. Así que el lado RxDart primero
convierte cada sonda en un stream interno
(Rx.fromCallable + onErrorReturn(false))
para contrabandear los fallos de vuelta al canal de datos como
valores marcadores. El lado FxDart no necesita paso de conversión,
porque no hay nada desde lo que convertir: un try/catch
dentro de map hace del desenlace un bool
justo donde ocurre, y el resto del pipeline es aritmética. Los mismos
operadores, una frontera de modelo menos que cruzar.
Benchmark
Caso async: la escala principal es N = 10,000, no 1,000,000. Cada elemento cuesta una vuelta del event loop en ambos lados, así que un millón de awaits reales mediría el event loop de Dart durante minutos — no el pipeline. Los retardos son de longitud cero y se conserva el límite de concurrencia del ejemplo; lo que comparan las barras es la maquinaria del pipeline.
N = 100
Tiempo Empate
Memoria pico Empate
N = 10,000
Tiempo Gana FxDart
Memoria pico Empate
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í.