Each call feeds the next

Toss-up async

Requirement

Four API steps run strictly one after another — login, profile, orders, invoice — and each request is built from the previous response (the session id feeds the profile call, the user id feeds the orders call, …). Print each step with its response. The fake API table is in the code; both versions must print the lines shown under Expected output.

Expected output
login -> session-9
profile -> user-42
orders -> order-7
invoice -> pdf-3

Side by side

RxDart

FxDart

Why they differ

Neither model has to fight for sequentiality here. RxDart's asyncMap pauses the source while each future runs, so the calls are serial by construction; a pull pipeline only ever asks for the next value after the previous one resolved, so it is serial by default. The interesting difference is where the dependency — the token each response hands the next request — lives.

The RxDart side threads it through a mutable variable the mapper closes over: idiomatic, compact, and slightly outside the pipeline — the data flow between steps is invisible to the operator chain. The FxDart side threads it through scan's accumulator, so the previous response is an explicit input of the next step; the cost is that scan emits its seed, which the printout has to skip. One hidden variable versus one skipped seed line — a genuine tie, decided by whether you prefer state captured in a closure or state visible in the fold.

Benchmark

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

Async case: the headline scale is N = 10,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

RxDart 432 µs
FxDart 415 µs

Peak memory Tie

RxDart 16.5 MB
FxDart 16.5 MB

N = 10,000

Time Tie

RxDart 35.9 ms
FxDart 36.1 ms

Peak memory Tie

RxDart 28.2 MB
FxDart 28.6 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.