Skip to content
Channels and Flow
step 1/5

Reading — step 1 of 5

Learn

~1 min readCoroutines

Coroutines need ways to talk. Kotlin offers two:

Channel<T> — like a Go channel. Producers send, consumers receive:

import kotlinx.coroutines.channels.*

fun main() = runBlocking {
    val ch = Channel<Int>()
    launch {
        for (i in 1..5) ch.send(i * i)
        ch.close()
    }
    for (sq in ch) {
        println(sq)
    }
}

for-in over a channel receives until close. Channels are lazy and one-shot — once consumed, the value is gone.

Flow<T> — cold async stream. Like a sequence but with suspend:

import kotlinx.coroutines.flow.*

fun squares(n: Int): Flow<Int> = flow {
    for (i in 1..n) {
        delay(10)            // can suspend inside
        emit(i * i)
    }
}

fun main() = runBlocking {
    squares(5).collect { value ->
        println(value)
    }
}

Cold = doesn't run until collected. Each collect re-runs the producer. Compare to channels which are hot.

Flow operators:

flow.map { it * 2 }
    .filter { it > 5 }
    .take(3)
    .collect { println(it) }

flowOn(dispatcher) — change which thread the upstream runs on:

flow { ... }
    .flowOn(Dispatchers.IO)         // upstream on IO
    .map { ... }                     // map on whatever collected with
    .collect { ... }

StateFlow / SharedFlow — hot variants:

  • StateFlow<T> — reactive state holder, always has a value
  • SharedFlow<T> — multi-subscriber event stream

Used heavily in Android with Jetpack Compose / ViewModels.

Channels vs Flow:

  • Channel — for hand-off between specific coroutines. Hot.
  • Flow — for emission streams transformed with operators. Cold (or use Shared/StateFlow for hot).

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…