Laminar v17.2.1
Airstream bug fixes!
Laminar is a Scala.js library for building web application interfaces and managing UI state. Learn more in a few short examples or one long video.
Releases
Airstream Bug Fixes
Laminar 17.2.1 release consists entirely of two Airstream bug fixes. The fix slightly changes Airstream behaviour, although you are unlikely to be relying on the current incorrect behaviour.
Fixed: flattenSwitch / flatMapSwitch unwanted restart issue
When flattenSwitch (or flatMapSwitch) operator switches from signal1 to signal2, it first disconnects from signal1 before connecting to signal2. Usually this is the right thing to do.
Our code already had a special case – we don't do this if signal1 == signal2 – in that case we don't need to restart the same signal for no reason, so we just keep it active, without power-cycling it.
However, there is a more advanced variation of this special case that we failed to consider previously – if both signal1 and signal2 have a common ancestor signal0, and signal0 has no other observers, switching from signal1 to signal2 will cause signal0 to briefly stop and then re-start.
This is undesirable because some signals may have custom onStart logic in them – for example, EventStream.fromValue(1).toSignal(0) will emit 1 every time it's started (by design), so we don't want to re-start such a signal when the user does not explicit mean to, as it would produce an unexpected event – a glitch.
The 17.2.1 release fixes this with a make-before-break mechanism – now, when flattenSwitch switches from signal1 to signal2, it first connects to the new signal2 source before disconnecting from the old signal1 source. This ensures that any common ancestors of the old and new signals remain active and are not re-started unnecessarily.
All of this applies to streams just as well as signals.
Migration: You may have flattenSwitch / flatMapSwitch signals (or streams) that currently emit extra events due to this bug, and they will stop emitting those events. I believe those glitchy events are entirely undesirable, so if everything is working fine for you right now, chances are it will continue working fine, but if you have experienced weird / unexplained events when flattening observables, this may just fix it.
Fixed: split signal's child looking at inactive split signal
SplitChildSignal (the signal that the split operator's callback gives you for each item) reads the internal state of SplitSignal (the result of the .split operator itself) to do its thing, but does not formally depend on it as an observable.
This means that if SplitChildSignal has observers, and SplitSignal does not, then SplitSignal is not running and so it is not updating its internal state. IN this scenario, SplitChildSignal can potentially read stale/incorrect state from SplitSignal, resulting in SplitSignal emitting a stale/incorrect version of its item, or failing to emit when it's due.
We fixed this by properly emulating SplitChildSignal's dependency on SplitSignal. Our SplitChildSignal is perhaps the weirdest signal in Airstream on a technical/implementation level, so it needed special care for this.
Migration: In practice, you are unlikely to encounter this bug when using typical Laminar patterns. And if you did, you would likely notice SplitChildSignal emitting incorrect values.
Artūras Šlajus has provided a great example of these two bugs working together to sabotage two nested split-s followed by a map and a flattenSwitch.
Thank you
Laminar development is kindly supported by my sponsors, and I am very grateful to be able to work on all this.
DIAMOND sponsor:
GOLD sponsors: