`suspend fun foo(): Flow<T>` I once read if a fun ...
# coroutines
s
suspend fun foo(): Flow<T>
I once read if a fun returns Flow, it shouldn't marked suspend. But I cannot remember what could go wrong with it. Anyone?
j
It's not really that something can go wrong, it's just that these 2 approaches have 2 opposite conventions: • a
suspend
function is expected to do all its work before returning • a function returning
Flow
is supposed to return quickly without doing anything, because the
Flow
collection later on is supposed to trigger the work (and is itself suspend) So if a function has both
suspend
and
Flow
return type, it's unclear what its contract is (and it should be documented). What's your use case?
s
I think of a regular function returning a Flow of values is like a suspend function returning a value; but instead of one value, the Flow returns possibly many
j
Not exactly. I would argue that it would be true if you included the flow collection in your comparison. Thus, the parallel is rather between
functionReturningFlow().collect { ... }
and
suspendFunction()
, not between
functionReturningFlow()
and
suspendFunction()
.
And that is the crux of why
functionReturningFlow()
is not suspending, but
collect()
is
s
True, it missed some details 🙂 : One value => suspend fun Many values => regular fun + .collect
r
For a practical example you can always turn
Copy code
{
  someSuspendingFun()
  return someFlow
}
into
Copy code
flow {
  someSuspendingFun()
  emitAll(someFlow)
}
which doesn't require suspend so is simpler for consumers
j
One value => Lazy Deferred Many values => Flow
neither should suspend
j
Deferred is usually not just lazy, though. It means some computation has been launched in a scope you don't control, so the signature
fun getSomething(): Deferred<X>
is not great unless the scope is extremely clear from the context. But why complicate things for a single value? The whole point of coroutines is that they are simple to use thanks to suspend functions
👍 2
💯 2
👍🏾 1
s
For the above, to make the scope clear, i'd do
fun CoroutineScope.getSomething() : Deferred<X>
j
but even then, the signature doesn’t indicate whether or not the Deferred is lazy
s
Correct; for that you'll have to call CoroutineScope.async your self with the proper start-properties
j
..and thus you may as well define the function as a simple suspend fun, and let the caller decide the scope and the coroutine
👍 1
s
Thanks heap guys!