Compare mapLatest and transformLatest with map/transform. What exactly gets cancelled, and how do they relate to collectLatest?
answer
- map/transform finish each value; Latest variants cancel previous
- mapLatest = 1→1, transformLatest = 1→many (uses emit)
- Intermediate (return Flow) vs collectLatest terminal
- Same cooperative-cancellation rule
- flatMapLatest = switch to new inner flow, cancel old
basics
~20 smap and transform run their lambda fully for each value. mapLatest and transformLatest start the lambda for each value but cancel the previous lambda when a new value comes in. They are the same 'latest wins' idea as collectLatest, but as intermediate operators that produce a new Flow.
solid answer
~50 smap/transform are intermediate operators: for each upstream value they run the transform fully and emit downstream. mapLatest and transformLatest run the same transform but cancel the in-flight transform coroutine when a newer upstream value arrives, so only the latest transform completes and emits. transformLatest gives the block a FlowCollector (via emit), so it can emit zero, one, or many values per input — but a fresh upstream value cancels any pending emissions from the previous block. mapLatest is the special case of one value out per input. They are 'Latest' siblings of collectLatest: same cancel-previous semantics, but they yield a Flow you can chain further instead of being terminal. Same cooperative-cancellation rule applies — the transform needs a suspension point. Classic use: flatMapLatest for switching to a new inner flow (e.g. new search query) is the related higher-order operator.
code
kotlin · 13 linesimport kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
flowOf(1, 2, 3)
.transformLatest { v ->
emit("start-$v")
delay(100)
emit("end-$v") // only end-3 survives
}
.collect { println(it) }
// start-1, start-2, start-3, end-3
}go deeper
Knows the Latest variants cancel the previous block like collectLatest.
Distinguishes mapLatest (1→1) from transformLatest (1→many emit) and intermediate vs terminal.
Connects them to flatMapLatest for inner-flow switching and recalls the shared cooperative-cancellation caveat.
Chooses between mapLatest/transformLatest/flatMapLatest by the unit of cancellation (transform vs full subscription) and side-effect safety.
## Operator families - **`map { v -> ... }`** — intermediate operator; for each upstream value runs the lambda to completion and emits exactly one transformed value downstream. - **`transform { v -> emit(...) }`** — intermediate; the lambda is a `suspend FlowCollector<R>.(T) -> Unit`, so you can `emit` **zero or more** values per input. `map` and `filter` are built on it. - **`collect`** — terminal; consumes values, emits nothing downstream. ## The 'Latest' variants Each has a `...Latest` sibling that adds cancel-previous semantics: | Plain | Latest | Kind | |---|---|---| | `map` | `mapLatest` | intermediate, 1→1 | | `transform` | `transformLatest` | intermediate, 1→many | | `collect` | `collectLatest` | terminal | In all three, the block for a value runs in a child coroutine; when a **newer upstream value arrives**, that child is **cancelled** and a new one starts. Only the latest input's block runs to completion (and, for the intermediate ones, only its emissions reach downstream). ```kotlin flowOf(1, 2, 3) .mapLatest { v -> delay(100); v * 10 } .collect { println(it) } // prints only 30 ``` For `transformLatest`, pending `emit`s from a superseded block never reach downstream: ```kotlin flowOf("a", "b") .transformLatest { s -> emit("$s-1") delay(50) emit("$s-2") // for "a" this is cancelled by "b" } .collect { println(it) } // a-1, b-1, b-2 ``` ## Relationship to collectLatest They share **identical cancellation semantics**; the only difference is position in the pipeline: `mapLatest`/`transformLatest` are **intermediate** (return a new `Flow`, can be chained), while `collectLatest` is **terminal** (consumes the flow). Same **cooperative-cancellation** caveat: the transform needs a suspension point to actually be interruptible. ## Related: flatMapLatest `flatMapLatest { v -> someFlow(v) }` is the higher-order cousin: it switches to a **new inner flow** per upstream value and cancels the previous inner flow's collection. It is the canonical 'restart the request on each new query' operator. `mapLatest` cancels a transform; `flatMapLatest` cancels a whole inner-flow subscription.
- What can transformLatest do that mapLatest cannot?transformLatest gives a FlowCollector so its block can emit zero, one, or many values per input; mapLatest emits exactly one.
- How is flatMapLatest different from mapLatest?flatMapLatest switches to and collects a new inner Flow per value, cancelling the previous inner flow; mapLatest just cancels and reruns a transform that yields a single value.
saying these in an interview costs you the question
- Saying mapLatest can emit multiple values per input (that is transformLatest)
- Calling collectLatest an intermediate operator
- Forgetting the transform also needs a suspension point to be cancellable
- Confusing flatMapLatest (inner-flow switch) with mapLatest
- Thinking the Latest variants run transforms in parallel