Búsqueda en vivo sobre un stream de pulsaciones

Gana FxDart async

Requisito

Un cuadro de búsqueda emite cada pulsación de tecla como un Stream de Dart —alguien escribiendo hacia darts, con algunos valores repetidos por la repetición automática del teclado (secuencia fija en el código de abajo). Conviértelo en búsquedas de backend: descarta las consultas de menos de dos caracteres, no busques nunca dos veces la misma consulta, detente después de cuatro consultas buscadas e imprime cada consulta con su número de resultados y el mejor resultado —además de cuántas llamadas al backend se hicieron realmente.

Con fxStream el stream de pulsaciones se convierte en un pipeline y cada regla se convierte en un operador: filter para el mínimo de longitud, uniq para las repeticiones, take(4) para el presupuesto, y después map realiza la búsqueda. Como take está antes del paso de búsqueda y la cadena está basada en pull, se producen exactamente cuatro llamadas al backend y la cola del stream no se consume nunca.

Salida esperada
live search over the keystroke stream:
  'da' -> 5 hits (top: dart language tour)
  'dar' -> 5 hits (top: dart language tour)
  'dart' -> 5 hits (top: dart language tour)
  'darts' -> 1 hit (top: darts scoring rules)
backend searches: 4

Lado a lado

Dart nativo

FxDart

Por qué difieren

El bucle nativo await for es compacto —pero fíjate en dónde han acabado las reglas: el mínimo de longitud y la deduplicación comparten una única expresión continue (q.length < 2 || !seen.add(q), que cuela una mutación dentro de una condición), y el presupuesto es una comprobación de contador con un break. Tres políticas comprimidas en dos cláusulas de guarda; añadir una cuarta implica desenredarlas. El pipeline gasta un operador con nombre por regla, en el orden en que se aplican, y esa misma cadena aceptaría sin cambios el stream de cambios de texto de un widget real. Una advertencia honesta: el debounce de fxdart es una utilidad para llamadas a funciones, no un operador de stream —silenciar por tiempo un stream parlanchín es una herramienta distinta de las cuatro reglas que se ven aquí.

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 52 µs
FxDart 71 µs

Memoria pico Empate

Dart nativo 16.4 MB
FxDart 16.6 MB

N = 10,000

Tiempo Gana nativo

Dart nativo 4.44 ms
FxDart 5.44 ms

Memoria pico Empate

Dart nativo 23.1 MB
FxDart 23.2 MB

N = 100,000

Tiempo Gana nativo

Dart nativo 45.2 ms
FxDart 56.0 ms

Memoria pico Empate

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