Skip to content

Step 1 of 5 · Reading · ~2 min

Learn

Coroutines

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).

What this grader actually has

Channel and Flow both live in kotlinx.coroutines, a library that is not on this grader's classpath — import kotlinx.coroutines.flow.* fails with unresolved reference: kotlinx. The stdlib's Sequence is the closest thing you can run here:

val total = listOf(1, 2, 3, 4, 5).asSequence()
    .filter { it % 2 == 0 }
    .map { it * it }
    .toList()
    .sum()                // 20 — nothing ran until toList() asked

A Sequence is cold and lazy exactly like a Flow: the pipeline does nothing until a terminal operation pulls on it, and each terminal operation re-runs it. What a Flow adds on top is suspension — a Flow can delay, await I/O and be collected from a coroutine, which a Sequence cannot.

Up nextinline + reified GenericsInline + DSL Building

Discussion

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

Sign in to post a comment or reply.

Loading…