https://kotlinlang.org logo
#coroutines
Title
# coroutines
l

Lilly

08/23/2022, 8:48 PM
I'm using flow
combine
the first time trying to combine multiple flows to an single object. One of the flows return items which are part of a
List
. How can I get access to the previous combined object to accumulate the items of the list?
e

ephemient

08/23/2022, 9:08 PM
one option is to wrap the flows with something like
Copy code
fun <T, R> Flow<T>.withHistory(
    maxSize: Int,
    transform: suspend (List<T>) -> R,
): Flow<R> {
    require(maxSize > 0)
    return flow {
        val window = ArrayDeque<T>(maxSize)
        collect {
            if (window.size == maxSize) window.removeFirst()
            window.addLast(it)
            emit(window.toList())
        }
    }
}
if you only need one previous item,
Copy code
fun <T, R> Flow<T>.zipWithPrevious(
    transform: suspend (T?, T) -> R,
): Flow<R> = flow {
    var prev: T? = null
    collect { cur ->
        emit(transform(prev, cur))
        prev = cur
    }
}
l

Lilly

08/24/2022, 5:56 PM
Thanks @ephemient Another related question about `combine`: I use it to combine 4 Flows, 2 Flows come from
BroadcastReceivers
so they do not
emit
that frequently. In my logs, I can see that one of the Flows emit but the
transform
block of combine is not triggered. Does combine has some hidden behavior?
Copy code
fun startListening() {
        combine(
            bluetoothLocal.discoverDevices(),
            bluetoothLocal.discoveryState(), // emits a value but transform block is not triggered
            bluetoothLocal.bluetoothAdapterState(),
            protocolState,
        ) { discoveredDevice, isScanning, isBluetoothEnabled, protocolState ->
            ....
       }.launchIn(scope)
     }
I would expect that transform block is called everytime one of the flows emit a value, isnt that the case?
ok I got it. combine expects initial values for every flow before transform is triggered.
bluetoothLocal.bluetoothAdapterState()
does not has an initial value. When I emit an initial value like
bluetoothLocal.bluetoothAdapterState().onStart { emit(bluetoothLocal.isEnabled) }
it works like expected.
12 Views