このページはまだ翻訳されていないため、英語で表示されます。 翻訳に参加する

Parallel downloads, results in order

FxDart wins async

Requirement

Download six files — each with a fixed size and a fixed (simulated) transfer time, in the code below — with at most three downloads in flight, and list the results numbered in request order. The delays are chosen so completions interleave: the 30 ms video.mp4 is requested first but the 10 ms notes.txt finishes first. Both versions print which file finished first and the maximum observed concurrency — proving the work really overlapped, out of order, while the listing stayed in order.

That reorder-under-the-hood, order-on-the-surface guarantee is exactly what concurrent(3) does: it evaluates up to three upstream items at once and still yields results in source order. The chain numbers them with zipWithIndex and assembles the report with join.

Expected output
downloaded 6 files, 3 at a time, in request order:
  1. video.mp4 (900 KB)
  2. notes.txt (2 KB)
  3. slides.pdf (340 KB)
  4. photo.jpg (180 KB)
  5. data.csv (55 KB)
  6. theme.zip (260 KB)
first to finish: notes.txt
total size: 1737 KB
max downloads in flight: 3

Side by side

Native Dart

FxDart

Why they differ

Future.wait preserves order but downloads everything at once — no limit. Adding the limit is what forces the native worker pool, and preserving order under that pool is precisely the subtle part: the pre-sized results list indexed by a shared cursor. Get the slot bookkeeping wrong and results come back shuffled — a bug that only shows when completion order happens to differ from request order, which is timing-dependent and easy to miss in tests. In FxDart the ordering guarantee is the operator's contract, not your code: concurrent(3) cannot return items out of order, no matter how the timings fall.

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 373 µs
FxDart 444 µs

Peak memory Tie

Native Dart 16.5 MB
FxDart 17.1 MB

N = 10,000

Time Native wins

Native Dart 31.8 ms
FxDart 37.3 ms

Peak memory FxDart wins

Native Dart 50.8 MB
FxDart 33.0 MB

N = 100,000

Time Native wins

Native Dart 338.8 ms
FxDart 380.5 ms

Peak memory Native wins

Native Dart 80.9 MB
FxDart 92.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.