Combining flows: merge, zip, and combine
This is a chapter from the book Kotlin Coroutines. You can find it on LeanPub or Amazon.
Let's talk about combining two flows into one. There are a few ways to do this. The simplest involves merging the elements from two flows into one. No modifications are made, no matter from which flow elements originate. To do this, we use the top-level merge
function.
It is important to know that when we use merge
the elements from one flow do not wait for another flow. For instance, in the example below, elements from the first flow are delayed, but this does not stop the elements from the second flow.
We use merge
when we have multiple sources of events that should lead to the same actions.
The next function is zip
, which makes pairs from both flows. We also need to specify a function that decides how elements are paired (transformed into one what will be emitted in the new flow). Each element can only be part of one pair, so it needs to wait for its pair. Elements left without a pair are lost, so when the zipping of a flow is complete, the resulting flow is also complete (as is the other flow).
The
zip
function reminds me of the polonaise - a traditional Polish dance. One feature of this dance is that a line of pairs is separated down the middle, then these pairs reform when they meet again.
The last important function when combining two flows is combine
. Just like zip
, it also forms pairs from elements, which have to wait for the slower flow to produce the first pair. However, the similarities to the polonaise dance end here. When we use combine
, every new element replaces its predecessor. If the first pair has been formed already, it will produce a new pair together with the previous element from the other flow.
Notice that zip
needs pairs, so it closes when the first flow closes. combine
does not have such a limitation, so it will emit until both flows are closed.
combine
is typically used when we need to actively observe two sources of changes. If you want to have elements emitted whenever a change occurs, you can add initial values to each combined flow (to have the initial pair).
A typical use case might be when a view needs to be either of two observable element changes. For example, when a notification badge depends on both the current state of a user and some notifications, we might observe them both and combine their changes to update a view.