Gran final — cierre mensual de DailyLedger

Gana FxDart async

Requisito

Cierra el mes de un libro de cuentas personal. Diez entradas viven en un almacén (datos fijos en el código de abajo); carga cada una por id —como máximo tres cargas en curso, tal como demuestra el contador de máximo en curso— y luego calcula el cierre de julio de 2026: quédate solo con las entradas de julio (una rezagada es de junio), separa los ingresos de los gastos, suma cada lado e imprime el neto junto con las tres categorías de gasto principales y su número de entradas.

Las formas que ves aquí están sacadas de una app real: el modelo Entry, la separación ingresos/gastos (filterpartitionsumBy en cada mitad) y el desglose por categoría (groupBysumBy por grupo → sortBy descendente → take(3)) son un calco de los pipelines monthSummary y categoryBreakdown de DailyLedger, con la fase de carga asíncrona (toAsyncmapconcurrent(3)) por delante.

Salida esperada
July 2026 close — 10 entries loaded, 3 at a time (max in flight: 3)
income  $3600.00
expense $1478.15
net     $2121.85
top spending categories:
  housing: $1150.00 (1 entry)
  food: $206.35 (3 entries)
  utilities: $106.30 (2 entries)

Lado a lado

Dart nativo

FxDart

Por qué difieren

Cincuenta ejemplos después, este es el patrón al que todos apuntaban: Dart nativo necesita tres dialectos para una sola funcionalidad —los helpers de package:collection para agrupar, fold con valores iniciales explícitos para los totales, y un pool de workers escrito a mano en cuanto la fase de carga necesita un límite de concurrencia—. Cada pieza está bien por separado; juntas convierten la lógica de negocio en lo más difícil de encontrar en la pantalla. La versión con FxDart usa el mismo vocabulario desde la fase de carga hasta el informe, y como cada etapa es un pipeline puro, se puede extraer cualquiera de ellas y probarla de forma unitaria como entran entradas, salen datos de vista.

Estos pipelines no son un adorno de demostración: son exactamente cómo la app de demostración DailyLedger calcula su panel —mismo modelo, mismos operadores, funcionando en vivo en tu navegador. Si las cincuenta comparaciones te enseñaron las palabras, DailyLedger es la frase que iban construyendo.

Benchmark

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

Caso async: la escala principal es N = 20,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

Dart nativo 371 µs
FxDart 401 µs

Memoria pico Empate

Dart nativo 16.6 MB
FxDart 17.2 MB

N = 10,000

Tiempo Empate

Dart nativo 318.8 ms
FxDart 325.6 ms

Memoria pico Empate

Dart nativo 50.3 MB
FxDart 50.2 MB

N = 20,000

Tiempo Empate

Dart nativo 1179.1 ms
FxDart 1192.2 ms

Memoria pico Empate

Dart nativo 52.6 MB
FxDart 54.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í.