0Pricing
Kotlin Academy · Lekcja

combine i zip: łączenie wielu Flow

Łącz wiele obiektów Flow i zestawiaj razem ich emisje za pomocą zip.

combine i zip: łączenie wielu Flow to bezpłatna lekcja Kotlin Academy na CoddyKit. To lekcja 3 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Kotlin Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Dlaczego łączyć przepływy?

Rzeczywiste aplikacje często muszą łączyć wiele strumieni danych: preferencje użytkownika + dane sieciowe, pola formularza + stan walidacji. Kotlin Flow udostępnia do tego celu operatory zip, combine i merge.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val names = flowOf("Alice", "Bob", "Carol")
    val ages  = flowOf(30, 25, 35)
    names.zip(ages) { name, age -> "$name is $age" }
         .collect { println(it) }
}

zip: emisje parami

zip łączy emisje w pary, zachowując kolejność i zasadę jeden do jednego. Kończy działanie, gdy zakończy się krótszy przepływ.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = flowOf(1, 2, 3, 4)   // 4 elements
    val b = flowOf("a", "b", "c")  // 3 elements — zip stops here
    a.zip(b) { n, s -> "$n-$s" }
     .collect { println(it) } // 1-a, 2-b, 3-c
}

combine: najnowsze wartości

combine emituje nową wartość za każdym razem, gdy dowolny z przepływów emituje wartość, korzystając z najnowszej wartości z każdego z nich. Oba przepływy muszą wyemitować wartość co najmniej raz.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val flow1 = MutableStateFlow(1)
    val flow2 = MutableStateFlow("a")
    combine(flow1, flow2) { n, s -> "$n-$s" }
        .take(1)
        .collect { println(it) } // 1-a
}

Różnica między combine a zip

zip: czeka na pasującą parę. combine: reaguje na każdą aktualizację, korzystając z najnowszych wartości. Należy używać combine dla reaktywnego interfejsu użytkownika (np. dwóch pól formularza), a zip do sekwencyjnego łączenia par.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    // zip: 1-a, 2-b
    val nums = flowOf(1, 2).zip(flowOf("a", "b")) { n, s -> "$n-$s" }
    nums.collect { println("zip: $it") }

    // combine fires more:
    combine(flowOf(1, 2), flowOf("a")) { n, s -> "$n-$s" }
        .collect { println("combine: $it") }
}

merge: przeplatanie przepływów

merge emituje wartości z wielu przepływów w miarę ich nadejścia, bez tworzenia par — kolejność wyników zależy od czasu emisji.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = flow { emit("A1"); delay(50); emit("A2") }
    val b = flow { delay(25); emit("B1") }
    merge(a, b).collect { println(it) } // A1, B1, A2
}

combine z co najmniej 3 przepływami

combine obsługuje wiele przepływów. Należy przekazać je jako vararg, a lambda otrzyma wszystkie najnowsze wartości.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val a = MutableStateFlow("hello")
    val b = MutableStateFlow(42)
    val c = MutableStateFlow(true)
    combine(a, b, c) { s, n, flag -> "$s-$n-$flag" }
        .take(1)
        .collect { println(it) } // hello-42-true
}

flatMapMerge: współbieżne przepływy wewnętrzne

flatMapMerge mapuje każdy element na przepływ, a następnie zbiera wszystkie przepływy wewnętrzne współbieżnie, łącząc ich wyniki.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    (1..3).asFlow()
        .flatMapMerge { n ->
            flow { delay(100L - n * 10); emit("result-$n") }
        }
        .collect { println(it) }
}

flatMapLatest: anulowanie po nowej emisji

flatMapLatest anuluje bieżący przepływ wewnętrzny, gdy nowy element pojawi się w przepływie źródłowym — idealnie sprawdza się przy wyszukiwaniu podczas pisania.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    flowOf("k", "ko", "kot", "kotl", "kotli", "kotlin")
        .flatMapLatest { query ->
            flow {
                delay(50)  // simulate debounced search
                emit("Result for: $query")
            }
        }
        .collect { println(it) } // only "Result for: kotlin"
}

zip do łączenia transakcji w pary

Należy używać zip do łączenia w pary zdarzeń z dwóch strumieni, np. działań użytkownika i potwierdzeń serwera, aby zapewnić zgodność jeden do jednego.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val orders = flowOf("Order1", "Order2", "Order3")
    val confirmations = flowOf("Confirmed", "Confirmed", "Rejected")
    orders.zip(confirmations) { order, status -> "$order: $status" }
          .collect { println(it) }
}

combine do walidacji formularza

Należy połączyć przepływy nazwy użytkownika i hasła, aby reaktywnie obliczać poprawność formularza — wynik jest aktualizowany za każdym razem, gdy zmieni się którekolwiek pole.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
    val username = MutableStateFlow("")
    val password = MutableStateFlow("")
    val isValid = combine(username, password) { u, p ->
        u.length >= 3 && p.length >= 8
    }
    username.value = "alice"
    password.value = "secure123"
    println(isValid.first()) // true
}

onEach + combine dla ładowania z wielu źródeł

Należy połączyć flagę ładowania z przepływem danych, aby utworzyć pojedynczy obiekt stanu interfejsu użytkownika z wielu niezależnych źródeł.

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
data class UI(val loading: Boolean, val data: String)
fun main() = runBlocking {
    val loading = MutableStateFlow(true)
    val data    = MutableStateFlow("")
    combine(loading, data) { l, d -> UI(l, d) }
        .take(2)
        .collect { println(it) }
    data.value = "result"
    loading.value = false
}

Szybkie sprawdzenie

Jaka jest główna różnica między zip a combine?

Podsumowanie

zip łączy emisje sekwencyjnie w pary; combine reaguje na każdą aktualizację, korzystając z najnowszych wartości; merge przeplata niezależne przepływy; flatMapLatest anuluje nieaktualne przepływy wewnętrzne. Wyboru należy dokonać w zależności od tego, czy potrzebne jest łączenie w pary, czy reaktywność.

Często zadawane pytania

Czy lekcja „combine i zip: łączenie wielu Flow” jest bezpłatna?

Tak — pełny tekst „combine i zip: łączenie wielu Flow” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Kotlin Academy, przejdź na CoddyKit PRO. Kurs Kotlin Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „combine i zip: łączenie wielu Flow”?

Łącz wiele obiektów Flow i zestawiaj razem ich emisje za pomocą zip. Ćwiczysz Kotlin Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Kotlin Academy?

Nie wymagamy żadnego doświadczenia. Kotlin Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 4.

Ile czasu zajmuje lekcja „combine i zip: łączenie wielu Flow”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Kotlin Academy?

Tak. Każda lekcja Kotlin Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Operatory Flow: map, filter, transform i take
  2. catch i onCompletion: obsługa błędów w Flow
  3. combine i zip: łączenie wielu Flow
  4. flowOn i buffer: kontekst i backpressure
← Powrót do Kotlin Academy