-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Flow firstOrNull support #1796
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Flow firstOrNull support #1796
Conversation
6b179a1
to
b8647a7
Compare
b8647a7
to
fd2ef83
Compare
What's your use-case for this function? Where does it show up? |
My use case is I have a Flow that could emit 0 or 1 elements matching a certain criteria, |
@dave08 I'm not sure I understand. Could you just use |
That throws if there's more than one element that matches |
@elizarov |
@LouisCAD Indeed, that difference between |
It isn't too sexy, but it works. suspend fun <T> Flow<T>.firstOrNull() = take(1).fold(null as T?) { _, v -> v } A generic version suspend fun <T> Flow<T>.getOrNull(index: Int) = take(index + 1).fold(null as T?) { _, v -> v }
suspend fun <T> Flow<T>.firstOrNull() = getOrNull(0) |
@elizarov I understand why you're puzzled now. I think I think it is out of the scope of this PR that is for If |
I'm not just puzzled. I'm looking for use-cases. This PR proposes to add |
@bradynpoulsen Can you give yours? |
I've been using a no-arg version of With Android Bluetooth, we can only set the callback once (upon connection), so all responses are returned as a stream (Flow) and we need to parse them out. Hence our use for We're writing some data over Bluetooth (discover services, write a descriptor, write a characteristic), then observing a Flow for the response. Packets can be dropped or something can happen on the peripheral's end, so there's no guarantee of actually getting that response. In interface GattFlows : BluetoothConnectionElement {
...
fun characteristicWrite(uuid: UUID): Flow<CharacteristicWrite>
...
} suspend fun sendCharacteristic(
characteristic: Characteristic,
data: ByteArray
) = withTimeoutOrNull(TIMEOUT) {
send(gattFlows.characteristicWrite(characteristic.uuid)) {
characteristic.value = data
gatt?.writeCharacteristic(characteristic)
}
}
internal suspend inline fun <E> send(
flow: Flow<GattEvent<E>>, crossinline writeBlock: () -> Unit = {}
): E? = withContext(executor) {
flow.onStart { writeBlock() }
.firstOrNull() // < -- Here's the use-case
?.let { response ->
if (response.isSuccessful) {
response.value
} else null
}
}
|
My original use case was to listen for the first finished I guess my case is really just for prevention of unnecessary crashing (which might not really be a concern, but why take that chance?) when not knowing a third party api well enough... I could technically use a try/catch for that though it might be less clear that I'm only really interested in 0 or 1 emissions (even though there could be more) that match the criteria. |
@dave08 Using |
4a49830
to
aff8202
Compare
Superseded by #1869 |
Adds implementations for
Flow
:suspend fun firstOrNull(): T?
suspend fun firstOrNull(predicate: suspend (T) -> Boolean): T?
It is not clear why they were intentionally left out in the original proposal (#1078) but introduced with
single
/singleOrNull