本页尚未翻译,因此以英文显示。 参与翻译

Fetch 10 profiles, 3 at a time

FxDart wins async

Requirement

From a directory of twelve accounts, take the active ones, order them by id, and fetch each profile from a (simulated) API — never more than three requests in flight, results in the original order. The fake fetch counts overlapping requests and both versions print the maximum observed, proving the limit held. The data is in the code below.

This is the flagship shape of the whole async section: a sync pipeline (filtersortBy) that crosses into async with toAsync and keeps going — map the fetch, concurrent(3) to bound it, one more map to format, join to finish. One chain from list to report.

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

Side by side

Native Dart

FxDart

Why they differ

Plain Dart handles the sync half fine (where + sortedBy), but at the async boundary the vocabulary runs out: bounding concurrency while preserving order means a hand-rolled worker pool — shared cursor, pre-sized result slots, a Future.wait over the workers. That pool is real production boilerplate, and it splits the task into two dialects: a fluent chain for prep, then imperative plumbing for the fetch. In the FxDart version the policy stays declarative end to end — concurrent(3) is the entire worker pool, and changing the limit (or dropping it) touches one number instead of the function's shape.

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

Peak memory Tie

Native Dart 16.4 MB
FxDart 16.6 MB

N = 10,000

Time Tie

Native Dart 28.7 ms
FxDart 30.1 ms

Peak memory FxDart wins

Native Dart 51.3 MB
FxDart 31.9 MB

N = 100,000

Time Tie

Native Dart 301.3 ms
FxDart 307.9 ms

Peak memory Tie

Native Dart 80.3 MB
FxDart 81.0 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.