Kotlin Academy · Lektion

flowOn og buffer: Kontekst og backpressure

Skift emissionskontekst med flowOn, og buffer emissioner for at håndtere backpressure.

Lektion 4 af 413 trin

flowOn og buffer: Kontekst og backpressure er en gratis Kotlin Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Kotlin Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Flow-kontekst

Som standard kører et flow i konteksten for den coroutine, der kalder collect. Producentens og forbrugerens dispatcher er den samme, medmindre du ændrer den.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        println("emit on: ${Thread.currentThread().name}")
        emit(1)
    }.collect {
        println("collect on: ${Thread.currentThread().name}")
    }
}

flowOn ændrer den opstrøms kontekst

flowOn(dispatcher) kører det opstrøms flow, altså alt over operatoren i kæden, på den angivne dispatcher, mens indsamlingen fortsætter på den kaldende coroutines dispatcher.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        println("emit: ${Thread.currentThread().name}")
        emit(1)
    }.map {
        println("map: ${Thread.currentThread().name}")
        it * 2
    }.flowOn(Dispatchers.Default)  // above runs on Default
    .collect {
        println("collect: ${Thread.currentThread().name}")
    }
}

Flere flowOn i en kæde

Du kan bruge flowOn flere gange. Hver forekomst påvirker operatorerne direkte over sig, frem til den forrige flowOn.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow { emit(1) }
        .map { it + 1 }.flowOn(Dispatchers.IO)      // map runs on IO
        .map { it * 2 }.flowOn(Dispatchers.Default)  // this map runs on Default
        .collect { println("Result: $it") }           // collect on Main (runBlocking)
}

Problemet med tilbagetryk

Når producenten udsender værdier hurtigere, end forbrugeren kan behandle dem, samler værdierne sig i en kø. Uden buffering får det producenten til at vente.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val time = System.currentTimeMillis()
    flow {
        repeat(3) { i ->
            delay(100)  // fast producer
            emit(i)
        }
    }.collect {
        delay(300)      // slow consumer
        println("Got $it in ${System.currentTimeMillis() - time}ms")
    }
}

Operatoren buffer()

buffer() kører producent og forbruger samtidigt i separate coroutines og gemmer udsendte værdier i en kanal. Producenten venter ikke på forbrugeren.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val time = System.currentTimeMillis()
    flow {
        repeat(3) { i -> delay(100); emit(i) }
    }.buffer()  // producer and consumer run concurrently
    .collect {
        delay(300)
        println("Got $it in ${System.currentTimeMillis() - time}ms")
    }
}

Bufferkapacitet

buffer(capacity) angiver kanalbufferens størrelse. Når bufferen er fuld, suspenderes producenten på grund af tilbagetryk. Standardkapaciteten er 64.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.channels.Channel
fun main() = runBlocking {
    flow { repeat(5) { emit(it) } }
        .buffer(Channel.RENDEZVOUS)    // 0: producer waits
        // .buffer(Channel.BUFFERED)   // default: 64
        // .buffer(Channel.UNLIMITED)  // unbounded
        .collect { delay(50); println(it) }
}

conflate() til kun den seneste værdi

conflate() fjerner mellemliggende værdier, når forbrugeren er langsom, og beholder kun den senest udsendte værdi. Det er nyttigt til UI-tilstand.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        repeat(5) { i -> emit(i); delay(50) }
    }.conflate()
    .collect { i ->
        delay(150)
        println("Collected: $i")  // skips some values
    }
}

collectLatest til langsomme forbrugere

collectLatest annullerer den aktuelle indsamlingsblok, når en ny værdi ankommer, og starter den igen med den seneste værdi.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flow {
        emit(1); delay(50)
        emit(2); delay(50)
        emit(3)
    }.collectLatest { value ->
        println("Processing $value")
        delay(100)  // gets cancelled if new value arrives
        println("Done $value")  // only prints for last value
    }
}

Mønsteret flowOn + buffer

Kombinér flowOn og buffer: kør producenten på IO til netværk eller disk, gem resultaterne i en buffer, og indsaml dem på Main.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun fetchItems(): Flow<String> = flow {
    repeat(3) { i ->
        delay(100)  // simulate IO
        emit("item-$i")
    }
}.flowOn(Dispatchers.IO).buffer(10)
fun main() = runBlocking {
    fetchItems().collect { println("UI: $it") }
}

channelFlow til samtidige producenter

channelFlow opretter et flow, der understøttes af en kanal, så flere coroutines kan udsende værdier samtidigt fra builderen.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun concurrentFlow(): Flow<Int> = channelFlow {
    launch { send(1) }
    launch { send(2) }
    launch { send(3) }
}
fun main() = runBlocking {
    concurrentFlow().collect { println(it) }
}

Vælg den rigtige strategi

Opsummering: flowOn til skift af kontekst, buffer til højere gennemløb, conflate til kun den seneste UI-værdi og collectLatest til at annullere forældet behandling.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
// Guidelines:
// CPU-heavy production  -> flowOn(Dispatchers.Default)
// IO-heavy production   -> flowOn(Dispatchers.IO)
// Slow consumer         -> buffer()
// UI state updates      -> conflate() or StateFlow
// Search/autocomplete   -> collectLatest or flatMapLatest
fun main() = runBlocking { println("Choose the right strategy!") }

Hurtigt tjek

Hvad påvirker flowOn i en flowkæde?

Opsummering

flowOn flytter arbejdet opstrøms til en anden dispatcher. buffer adskiller producent og forbruger for at øge gennemløbet. conflate fjerner mellemliggende værdier. collectLatest annullerer langsom behandling, når nye værdier ankommer.

Gratis at komme i gang

Lær Kotlin med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
51
Lektioner
203

Ofte stillede spørgsmål

Er lektionen “flowOn og buffer: Kontekst og backpressure” gratis?

Ja — alle 3 lektioner i læringssporet Kotlin Academy, inklusive “flowOn og buffer: Kontekst og backpressure”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Kotlin Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “flowOn og buffer: Kontekst og backpressure”?

Skift emissionskontekst med flowOn, og buffer emissioner for at håndtere backpressure. Du øver dig i Kotlin Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Kotlin Academy?

Der kræves ingen tidligere erfaring. Kotlin Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “flowOn og buffer: Kontekst og backpressure”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Kotlin Academy-lektion?

Ja. Alle Kotlin Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Flow-operatorer: map, filter, transform og take
  2. catch og onCompletion: Fejlhåndtering i Flow
  3. combine og zip: Sammenfletning af flere Flows
  4. flowOn og buffer: Kontekst og backpressure
← Tilbage til Kotlin Academy