Эта страница ещё не переведена, поэтому показана на английском. Помогите с переводом

Live search over a keystroke stream

FxDart wins async

Requirement

A search box emits every keystroke as a Dart Stream — a user typing toward darts, with some values repeated by key autorepeat (fixed sequence in the code below). Turn that into backend searches: skip queries shorter than two characters, never search the same query twice, stop after four searched queries, and print each query with its hit count and top hit — plus how many backend calls were actually made.

With fxStream the keystroke stream becomes a pipeline, and each rule becomes an operator: filter for the length floor, uniq for the repeats, take(4) for the budget, then map performs the search. Because take sits before the search step and the chain is pull-based, exactly four backend calls happen and the tail of the stream is never consumed.

Expected output
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

Side by side

Native Dart

FxDart

Why they differ

The native await for loop is compact — but look at where the rules went: the length floor and the dedupe share one continue expression (q.length < 2 || !seen.add(q), which smuggles a mutation into a condition), and the budget is a counter check with a break. Three policies compressed into two guard clauses; adding a fourth means untangling them. The pipeline spends one named operator per rule, in the order they apply, and the same chain would accept a real widget's text-change stream unmodified. One honest caveat: fxdart's debounce is a function-call utility, not a stream operator — quieting a chatty stream by time is a different tool than the four rules shown here.

Benchmark

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

Async case: the headline scale is N = 100,000, not 1,000,000. Every element costs an event-loop turn on both sides, so a million real awaits would measure Dart's event loop for minutes — not the pipeline. Delays are zero-length and the example's concurrency limit is kept; what the bars compare is the pipeline machinery.

N = 100

Time Tie

Native Dart 52 µs
FxDart 71 µs

Peak memory Tie

Native Dart 16.4 MB
FxDart 16.6 MB

N = 10,000

Time Native wins

Native Dart 4.44 ms
FxDart 5.44 ms

Peak memory Tie

Native Dart 23.1 MB
FxDart 23.2 MB

N = 100,000

Time Native wins

Native Dart 45.2 ms
FxDart 56.0 ms

Peak memory Tie

Native Dart 52.8 MB
FxDart 54.8 MB

Bars are medians of repeated timed iterations in fresh processes per side (small N is batched for timer resolution). Sides within 5% of each other — or within 0.6 ms, a difference no person can perceive — count as a tie; close relative races are re-measured up to 5 runs. In an app, anything under a few milliseconds is invisible to the user regardless of which bar is shorter. Memory is peak process RSS. The Dart VM and the dataset are identical on both sides, so the difference between the two bars is what the pipeline itself holds onto.