Consulta concurrente de precios con fallback

Gana FxDart async

Requisito

Calcula el precio de un pedido de seis líneas. Cada SKU se consulta en el servicio de precios en vivo —con un máximo de tres consultas en vuelo—, pero al servicio le faltan algunos SKU y devuelve null para ellos; esas líneas recurren al precio de catálogo que lleva el propio artículo. Imprime cada línea con su precio en orden (señalando los fallbacks), el número de fallbacks, el total del pedido y la concurrencia máxima observada. Todos los datos están en el código de abajo.

La cadena de FxDart hace la consulta bajo concurrent(3) y después un segundo map aplica el fallback con un simple ??: la política de recuperación no es más que otro paso del pipeline, aguas abajo de la petición acotada.

Salida esperada
priced 6 items, 3 lookups at a time:
  SKU-100 x2 @ $8.49
  SKU-205 x1 @ $24.50 (catalog fallback)
  SKU-317 x3 @ $3.99
  SKU-408 x1 @ $119.00
  SKU-512 x4 @ $2.80 (catalog fallback)
  SKU-620 x2 @ $14.20
fallbacks used: 2
order total: $212.05
max lookups in flight: 3

Lado a lado

Dart nativo

FxDart

Por qué difieren

El fallback en sí es fácil en ambas versiones —?? es Dart—. Lo que le falta a Dart nativo es el paso previo: hacer las consultas de tres en tres con los resultados en el orden de entrada obliga a recurrir al modismo del pool de workers (cursor compartido, huecos predimensionados, Future.wait), y la lógica del fallback acaba enterrada dentro del cuerpo del worker, donde es más difícil de ver y de probar. En la versión con FxDart, la consulta y la recuperación son dos etapas separadas y visibles de una misma cadena —concurrent(3) es dueño del límite, el map siguiente es dueño de la política— y las líneas del resumen (filter + size para los fallbacks, sumBy para el total) reutilizan el mismo vocabulario que enseña el resto del sitio.

Benchmark

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

Caso async: la escala principal es N = 100,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 374 µs
FxDart 395 µs

Memoria pico Empate

Dart nativo 16.5 MB
FxDart 17.1 MB

N = 10,000

Tiempo Empate

Dart nativo 34.9 ms
FxDart 34.2 ms

Memoria pico Gana FxDart

Dart nativo 49.1 MB
FxDart 30.9 MB

N = 100,000

Tiempo Empate

Dart nativo 351.3 ms
FxDart 353.2 ms

Memoria pico Empate

Dart nativo 81.1 MB
FxDart 80.8 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í.