Obtener de 4 en 4, resultados en orden

Gana FxDart async

Requisito

Obtén ocho perfiles de usuario cuyos tiempos de respuesta difieren, manteniendo como mucho 4 peticiones en vuelo a la vez — e imprime los resultados en orden de origen (el usuario 1 primero), más el máximo de peticiones en vuelo observado como prueba del límite. Los retardos están en el código; las dos versiones deben imprimir las líneas que aparecen bajo Salida esperada.

Salida esperada
user#1
user#2
user#3
user#4
user#5
user#6
user#7
user#8
max in flight: 4

Lado a lado

RxDart

FxDart

Por qué difieren

Ambos lados acotan la concurrencia en un operador, y el contador compartido muestra que ambos alcanzan de verdad 4 en vuelo. La división es por el orden. flatMap(maxConcurrent: 4) es una fusión: emite cada resultado interno en el momento en que se completa, así que con estos retardos el usuario 7 (10 ms) se imprimiría antes que el usuario 1 (80 ms). Para cumplir el requisito el lado RxDart etiqueta cada resultado con su id, lo recoge todo y ordena al final — el orden que tenía la fuente lo destruye la fusión y hay que reconstruirlo a mano al terminar.

mapConcurrent(4, fetch) nunca pierde el orden en primer lugar. En un pipeline pull, la concurrencia es una propiedad de la demanda, no de la entrega: el operador lanza cuatro pulls solapados pero entrega los resultados aguas abajo en el orden en que se pidieron, reteniendo una llegada rápida y tardía hasta que sus predecesoras más lentas hayan salido. Acotado-y-ordenado es la forma que la mayoría del trabajo por lotes realmente quiere — resultados alineados con las entradas, límites de tasa respetados — y aquí es el comportamiento por defecto en lugar de una reconstrucción. Cuando el orden de terminación es lo que de verdad quieres, eso también existe — concurrentPool, el siguiente ejemplo — pero es la variante que eliges, no el comportamiento que deshaces.

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 767 µs
FxDart 377 µs

Memoria pico Empate

RxDart 16.6 MB
FxDart 17.2 MB

N = 10,000

Tiempo Gana FxDart

RxDart 60.9 ms
FxDart 32.3 ms

Memoria pico Gana FxDart

RxDart 51.7 MB
FxDart 22.2 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í.