I have a suspending function that returns a flow. ...
# coroutines
s
I have a suspending function that returns a flow. It seems like it should be trivial to make it a function that returns immediately. I think
flow { getSubscriptionFlow().collect { emit(it) } }
would work, but it looks weird. Is there a simpler way to write that?
j
Are you trying to change the function so it's not suspend anymore, or are you trying to build a new function that is not suspend and uses the suspending one? Also, why is the function suspending in the first place if it returns a
Flow
? By that I mean what suspending stuff does it do before returning the flow, and why is that stuff not part of the flow definition?
☝️ 1
s
I'm trying to make a function that returns a flow immediately. It's suspending because it does a little bit of async stuff to make the flow.
Specifically, it gets an access token from then starts listening on a graphql subscription that returns a flow
e
it should do that in the flow builder itself
j
I see. Then you could do:
Copy code
fun yourFun(): Flow<X> = flow {
    val token = fetchToken()
    emitAll(getSubscriptionFlow(token))
}
s
Ah, perfect! That's exactly what I was looking for. Thank you!
e
https://rules.sonarsource.com/kotlin/RSPEC-6309
Functions returning
Flow
or
Channel
should return the result immediately and may start a new coroutine in the background. As a consequence, such functions should not be suspending and if they launch a coroutine in the background, they should be declared as extension functions on
CoroutineScope
.
s
Makes sense.
g
::fetchToken.asFlow().flatMap(::getSubacriptionFlow)
🤔 1
Do not say it's a simpler from the reading point of view, but if you need more idiomatic way to create flow from a suspend function
j
But it's not exactly about creating a flow from a suspend function here, it's about removing suspend on a function with signature
suspend fun <T> x(): Flow<T>
s
Thanks. I think I'll stick with @Joffrey’s solution, as it's much clearer what's happening.
g
Yep, removing suspend is the right in general
s
In the end, it looks basically like this:
Copy code
fun observeTickets(): Flow<List<Ticket>> =
    flow {
        emitAll(
            withClient { client ->
                client.subscription().toFlow()
            }
        )
    }
I'm happy with the result. withClient has the signature
suspend fun <T> withClient((Client) -> T): T
👍 1
j
Note that the
with
prefix in kotlin functions usually implies that the thing is provided as a receiver rather than argument.
use
does provide the "thing" as argument to the lambda, on the other hand (but it implies the resource is created for the lambda and then closed after the lambda is done). So I'd suggest either
suspend fun <T> withClient(Client.() -> T): T
if there is no notion of cleanup, or
suspend fun <T> useClient((Client) -> T): T
if you actually close some resources. But that's nitpicking on style of course.
s
I appreciate it. I just changed the name, thanks again.
383 Views