onErrorReturn, onErrorResume & retry
Three depths of recovery: patch each error with a value, abandon the source for a fallback, or throw the whole stream away and rebuild it.
Lecture
Errors behave differently on the push side, and the difference trips
people up. In a pull pipeline an exception ends the iteration — there
is one failure and then nothing. In a Dart Stream an
error is just another event: it is delivered, and the
subscription carries on. A stream can emit ten errors and forty
values and still close normally.
That is why onErrorReturn(value) is a
per-error substitution rather than a one-shot rescue. Every
error becomes one value event and the stream keeps
going — right for a flaky sensor where a bad reading should become a
placeholder and the feed should survive.
onErrorResume(f) is the one-shot switch. On the
first error the source is cancelled outright and the
stream f builds from that error takes over for good —
the cache-on-network-failure move. Nothing more of the original
source is ever seen, and an error thrown by f itself is
forwarded rather than swallowed.
FxEvents.retry(factory, [count]) works one level up: it
does not patch a stream's errors, it rebuilds the stream.
On error the failed attempt is thrown away and factory()
is called again for a fresh subscription — the right shape when the
failure is the connection itself. The budget counts
re-subscriptions, so count: 2 allows at most
three attempts; when it runs out the last error is forwarded and the
stream closes. Events an attempt already emitted are not taken back,
so the factory should produce something replayable.
fxdart events layer, after Rx's onErrorReturn,
onErrorResume and Rx.retry. For failures
you want to model rather than recover from,
attempt moves them onto the
value channel as a typed Left — the events-layer
bridge to Either and
Raise.
Demo 1 · A value per error
Demo 2 · Abandoning the source for a fallback
Try it yourself
Exercise: rebuilding a flaky stream, with and without a budget.