0Pricing
Kotlin Academy · Lección

flowOn y buffer: contexto y contrapresión

Cambie el contexto de emisión con flowOn y almacene emisiones en buffer para gestionar la contrapresión.

flowOn y buffer: contexto y contrapresión es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.

Contexto de Flow

De forma predeterminada, un flujo se ejecuta en el contexto de la corrutina que llama a collect. El dispatcher del productor y el del consumidor son el mismo, a menos que lo cambie.

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 cambia el contexto ascendente

flowOn(dispatcher) ejecuta el flujo ascendente (todo lo que se encuentra por encima en la cadena) en el dispatcher especificado, mientras que la recopilación continúa en el dispatcher del código que llama.

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}")
    }
}

Varios flowOn en una cadena

Puede utilizar flowOn varias veces. Cada uno afecta a los operadores que se encuentran directamente por encima, hasta el flowOn anterior.

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

El problema de la contrapresión

Cuando el productor emite valores más rápido de lo que el recopilador puede procesarlos, los valores se acumulan en una cola. Sin almacenamiento en búfer, esto obliga al productor a esperar.

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")
    }
}

Operador buffer()

buffer() ejecuta el productor y el consumidor simultáneamente en corrutinas independientes y almacena los valores emitidos en un canal. El productor no espera al consumidor.

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")
    }
}

Capacidad de buffer

buffer(capacity) establece el tamaño del búfer del canal. Cuando el búfer está lleno, el productor se suspende (contrapresión). La capacidad predeterminada es 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() para conservar solo el último valor

conflate() descarta los valores intermedios cuando el recopilador es lento y conserva únicamente la emisión más reciente. Resulta útil para el estado de la interfaz de usuario.

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 para recopiladores lentos

collectLatest cancela el bloque de recopilación actual cuando llega un valor nuevo y lo reinicia con el valor más reciente.

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
    }
}

Patrón flowOn + buffer

Combine flowOn y buffer: ejecute el productor en IO (red o disco), almacene los resultados en el búfer y recopílelos en 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 para productores simultáneos

channelFlow crea un flujo respaldado por un canal, lo que permite que varias corrutinas emitan simultáneamente desde el builder.

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

Elegir la estrategia adecuada

Resumen: flowOn para cambiar de contexto, buffer para aumentar el rendimiento, conflate para conservar solo el último estado de la interfaz de usuario y collectLatest para cancelar el procesamiento obsoleto.

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!") }

Comprobación rápida

¿Qué parte de una cadena de flujo afecta flowOn?

Resumen

flowOn traslada el trabajo ascendente a otro dispatcher. buffer desacopla el productor y el consumidor para mejorar el rendimiento. conflate descarta los valores intermedios. collectLatest cancela el procesamiento lento cuando llegan nuevos valores.

Preguntas frecuentes

¿La lección «flowOn y buffer: contexto y contrapresión» es gratis?

Sí — el texto completo de «flowOn y buffer: contexto y contrapresión» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.

¿Qué aprenderé en «flowOn y buffer: contexto y contrapresión»?

Cambie el contexto de emisión con flowOn y almacene emisiones en buffer para gestionar la contrapresión. Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Kotlin Academy?

No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «flowOn y buffer: contexto y contrapresión»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?

Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Operadores de Flow: map, filter, transform y take
  2. catch y onCompletion: gestión de errores en Flow
  3. combine y zip: combinar varios Flows
  4. flowOn y buffer: contexto y contrapresión
← Volver a Kotlin Academy