Rastrear páginas hasta agotarlas

Gana FxDart async

Requisito

Una API de pedidos paginada devuelve tres pedidos por página y una lista vacía cuando los datos se acaban (página 4). Rastrea página a página hasta la página vacía, aplana los pedidos en una sola lista, e imprímelos junto a cuántas páginas se obtuvieron realmente — exactamente cuatro; el rastreo no debe pedir jamás la página 5. La API falsa está en el código; las dos versiones deben imprimir las líneas que aparecen bajo Salida esperada.

Salida esperada
order#1
order#2
order#3
order#4
order#5
order#6
order#7
order#8
order#9
pages fetched: 4

Lado a lado

RxDart

FxDart

Por qué difieren

La paginación es el modelo pull: obtén una página, mírala, decide si pedir otra. El lado FxDart escribe eso tal cual — un cursor sync* sin fin de números de página que solo avanza cuando el pipeline demanda el siguiente, map(fetchPage), takeWhile(isNotEmpty), aplanar. Nada acota el cursor porque la demanda es la cota: cuando takeWhile ve la página vacía simplemente deja de tirar, y la página 5 ni siquiera llega a generarse.

El lado de streams llega al mismo sitio, pero solo tomando prestada mecánica pull: un cursor async* sin fin — Dart llano en lugar de un operador Rx — pausado hacia un comportamiento dirigido por demanda por el backpressure de asyncMap, y un takeWhile cuya cancelación detiene el rastreo en la página vacía. Funciona, e imprime el mismo pages fetched: 4 — porque pausar, reanudar y cancelar son exactamente el canal de retorno del modelo de streams para simular «pregunta otra vez cuando estés listo». El lado pull no necesitó la simulación: la demanda es su modo normal. Los trabajos donde el estado del consumidor decide si debe existir más entrada tienen forma de pull, y este es su caso más limpio.

Benchmark

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

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

RxDart 24 µs
FxDart 22 µs

Memoria pico Empate

RxDart 16.6 MB
FxDart 17.1 MB

N = 10,000

Tiempo Empate

RxDart 1.83 ms
FxDart 1.54 ms

Memoria pico Empate

RxDart 23.2 MB
FxDart 23.0 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í.