Obtener 10 perfiles, de tres en tres

Gana FxDart async

Requisito

De un directorio de doce cuentas, toma las activas, ordénalas por id y obtén cada perfil de una API (simulada) —sin superar nunca tres peticiones en curso y con los resultados en el orden original. La petición falsa cuenta las peticiones que se solapan y ambas versiones imprimen el máximo observado, demostrando que el límite se respetó. Los datos están en el código de abajo.

Esta es la forma emblemática de toda la sección asíncrona: un pipeline síncrono (filtersortBy) que cruza al mundo asíncrono con toAsync y sigue adelante —map para la petición, concurrent(3) para acotarla, otro map para dar formato y join para terminar. Una sola cadena, de la lista al informe.

Salida esperada
fetched 10 profiles, 3 at a time:
  user#1 Ada <ada@example.com>
  user#2 Bram <bram@example.com>
  user#4 Dana <dana@example.com>
  user#5 Eli <eli@example.com>
  user#6 Fay <fay@example.com>
  user#7 Gus <gus@example.com>
  user#9 Ines <ines@example.com>
  user#10 Jun <jun@example.com>
  user#11 Kira <kira@example.com>
  user#12 Liam <liam@example.com>
max requests in flight: 3

Lado a lado

Dart nativo

FxDart

Por qué difieren

Dart nativo resuelve bien la mitad síncrona (where + sortedBy), pero en la frontera asíncrona se le acaba el vocabulario: acotar la concurrencia conservando el orden significa un pool de workers hecho a mano —cursor compartido, huecos de resultado predimensionados, un Future.wait sobre los workers—. Ese pool es boilerplate de producción de verdad, y parte la tarea en dos dialectos: una cadena fluida para la preparación y luego fontanería imperativa para las peticiones. En la versión de FxDart la política se mantiene declarativa de principio a fin —concurrent(3) es el pool de workers entero, y cambiar el límite (o eliminarlo) toca un número en lugar de la forma de la función.

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 285 µs
FxDart 316 µs

Memoria pico Empate

Dart nativo 16.4 MB
FxDart 16.6 MB

N = 10,000

Tiempo Empate

Dart nativo 28.7 ms
FxDart 30.1 ms

Memoria pico Gana FxDart

Dart nativo 51.3 MB
FxDart 31.9 MB

N = 100,000

Tiempo Empate

Dart nativo 301.3 ms
FxDart 307.9 ms

Memoria pico Empate

Dart nativo 80.3 MB
FxDart 81.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í.