The key to glitch prevention is the UpdateBarrier. This component takes care of starting and finishing event transactions. The inTransaction function is used to wrap all event handling, creating a transaction that lasts until the original event and all derived events (those created when passing the event through all the map
s, combine
s etc) have been processed.
In the end of the transaction, a flush occurs. During flush, all pending actions are performed. And what are these action then? They are actions registered using the whenDoneWith function, and are basically requests like "please run this function when the dependencies certain observable have been fully flushed". To satisfy these request, the UpdateBarrier then executes these delayed actions, from roots to leaves, making sure that the observables get flushed in the correct order, from the roots up. And by roots I mean the observables that have no dependencies on other ones, while leaves are the observables that no other observable depends on. To optimize performance, UpdateBarrier has a double bookkeeping of the pending actions.
Certain combinators, such as the combine*
family and takeUntil
, (both indirectly though) call the whenDoneWith
method to make sure glitch don't occur. In the case of combine
, that means emitting only one output event regardless of how many of its dependencies have emitted an event during the transaction. In the case of takeUntil
it means that nothing is emitted if both the "source" and "stopper" observables have emitted during the transaction.
Flush occurs at the end of the transaction including all the derived events.
The badly named
pushIt
method doesn't traverse into dependencies, but merely flushes possibly queued events out. This is something that occurs in the scope of a single observable, within a transaction.I'll try to clarify the overall algorithm below.
1.1 Updates to external subscribers are deferred by the
subscribe
method, to be flushed after the transaction is complete, in phase 31.2 In some multi-dependent combinators like
combine
, also internal processing is deferred to phase 2 usingwhenDoneWith
to prevent glitches.whenDoneWith
are processed in a roots-first order, as described in the Gist.