Separar éxitos de fallos
Requisito
Siete pedidos de la importación 2026-08 pasan por una validación
async que lanza para dos de ellos (una dirección de envío ausente, un
SKU desconocido). Conserva ambos desenlaces: imprime
una línea ok: por pedido válido, y luego el conteo de
fallos. Los datos están en el código; las dos versiones deben
imprimir las líneas que aparecen bajo Salida esperada.
Salida esperada
ok: order #1001 ok: order #1002 ok: order #1004 ok: order #1005 ok: order #1007 failed: 2
Lado a lado
RxDart
FxDart
Por qué difieren
Un stream tiene dos canales — datos y error — y el canal de errores
tiene semántica de stream completo: una sola validación lanzada
mataría el pipeline con cinco pedidos aún sin procesar. Así que el
lado RxDart no puede simplemente asyncMap(validate);
envuelve cada validación en su propio stream interno
(Rx.fromCallable), captura en ese canal de errores
interno con onErrorReturnWith, y re-codifica el fallo
como un valor de datos antes de fusionar de vuelta. La recuperación
funciona, pero es fontanería de canales: el error tuvo que abandonar
la vía de datos solo para ser escoltado de regreso.
El lado FxDart nunca pone los fallos en un canal aparte. Un try/catch
dentro de map convierte cada desenlace en un registro
llano — (id, error?) — y a partir de ahí
partition es una división por predicado corriente. Esta
es la postura general del modelo pull ante los errores: son
valores que fluyen por el mismo pipeline tipado que todo lo
demás, así que mantener juntos éxitos y fallos no cuesta nada. Cuando
los desenlaces son parte del resultado y no una interrupción, el
modelo sin canal de errores privilegiado tiene menos que deshacer.
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 Gana RxDart
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í.