Flow adalah aliran nilai asynchronous yang dingin (cold stream), yaitu rangkaian nilai nol hingga banyak yang diproduksi satu per satu setelah alirannya dikumpulkan. Sebuah fungsi suspend mengirim satu nilai, sedangkan Flow mengirim banyak nilai secara berurutan dengan gaya yang sama. Topik ini membahas builder flow dan operator terminal collect, sifat cold stream beserta buktinya, serta operator antara seperti filter dan map yang disusun berantai.
Builder flow dan collect#
Aliran dibuat dengan builder flow, yaitu fungsi yang isinya memanggil emit untuk setiap nilai yang dikirim ke aliran. Setiap pemanggilan emit boleh dijeda, misalnya oleh delay atau pekerjaan jaringan, sehingga produksi nilai dapat berjalan sambil menunggu. Fungsi yang mengembalikan Flow biasanya dideklarasikan tanpa suspend karena builder-nya baru dijalankan saat aliran dikumpulkan.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun bacaSensor(): Flow<Int> = flow {
for (suhu in listOf(21, 22, 23)) {
emit(suhu)
}
}
fun main() = runBlocking {
bacaSensor().collect { suhu ->
println("Suhu terbaca: $suhu")
}
}# compiler Kotlin dan library coroutines yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export LIBS=/home/kal/my-project/tools/libs
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi main.kt dengan library coroutines pada classpath, lalu jalankan programnya
kotlinc main.kt -cp "$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar" -d out.jar && java -cp "out.jar:$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar:$STDLIB" MainKtSuhu terbaca: 21
Suhu terbaca: 22
Suhu terbaca: 23Fungsi collect adalah operator terminal, yaitu operator yang benar-benar menyalakan aliran dan menerima setiap nilai di dalam lambda pemakainya. Tanpa operator terminal, tidak ada satu baris builder pun yang berjalan, karena Flow menyimpan cara memproduksi nilai dan belum menjalankannya. Nilai juga diproses satu per satu secara berurutan, sebagaimana keluarannya yang tercetak sesuai urutan emit.
Flow sebagai cold stream#
Flow bersifat dingin, yang berarti builder-nya dieksekusi ulang dari awal untuk setiap pengumpulan. Aliran tidak menyimpan nilai maupun hasil sebelumnya, melainkan menyimpan resepnya, dan setiap pihak yang mengumpulkan mendapat eksekusi resep yang baru. Sifat ini dibuktikan dengan mencetak sesuatu di dalam builder, lalu mengumpulkan aliran yang sama dua kali.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun buatAngka(): Flow<Int> = flow {
println("Builder dieksekusi")
emit(1)
emit(2)
}
fun main() = runBlocking {
println("Collect pertama dimulai")
buatAngka().collect { nilai -> println("Nilai: $nilai") }
println("Collect kedua dimulai")
buatAngka().collect { nilai -> println("Nilai: $nilai") }
}# kompilasi dingin.kt dengan library coroutines pada classpath, lalu jalankan programnya
kotlinc dingin.kt -cp "$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar" -d dingin.jar && java -cp "dingin.jar:$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar:$STDLIB" DinginKtCollect pertama dimulai
Builder dieksekusi
Nilai: 1
Nilai: 2
Collect kedua dimulai
Builder dieksekusi
Nilai: 1
Nilai: 2Baris Builder dieksekusi tercetak dua kali, masing-masing tepat setelah collect dimulai, bukan saat fungsi buatAngka dipanggil. Urutan itu memperlihatkan dua sifat sekaligus, yaitu aliran baru berjalan ketika dikumpulkan dan setiap pengumpulan mendapat eksekusi penuh yang baru. Konsekuensinya, dua pengumpulan pada aliran dingin tidak berbagi keadaan apa pun, sehingga aliran semacam ini cocok untuk membaca ulang sumber data, bukan untuk dipakai berbagi kejadian antar banyak penerima.
Operator antara sumber dan collect#
Operator antara dipasang di antara sumber dan operator terminal, dan setiap operator mengembalikan Flow baru yang masih dingin. Operator filter meloloskan nilai tertentu, sedangkan map mengubah setiap nilai menjadi bentuk lain, dan keduanya dapat dirangkai sebagaimana operator collection atau Sequence. Aliran dirangkai berantai pada program berikut, yang menyaring pembacaan sensor lalu mengubah nilainya menjadi teks.
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
fun main() = runBlocking {
val suhu = flowOf(21, 30, 19, 28, 35)
suhu
.filter { it > 25 }
.map { "suhu $it derajat" }
.collect { println("Terdeteksi $it") }
}# kompilasi operasi.kt dengan library coroutines pada classpath, lalu jalankan programnya
kotlinc operasi.kt -cp "$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar" -d operasi.jar && java -cp "operasi.jar:$LIBS/kotlinx-coroutines-core-jvm-1.11.0.jar:$STDLIB" OperasiKtTerdeteksi suhu 30 derajat
Terdeteksi suhu 28 derajat
Terdeteksi suhu 35 derajatDari lima pembacaan, hanya tiga yang lolos ambang 25 dan semuanya sudah dalam bentuk teks saat diterima collect. Prosesnya berlangsung satu nilai pada satu waktu, yaitu satu nilai mengalir melewati filter lalu map sebelum nilai berikutnya diproduksi, serupa dengan evaluasi lazy pada Sequence. Satu operator lagi yang sering dipakai adalah flowOn, yaitu operator yang mengubah dispatcher tempat builder dijalankan tanpa memengaruhi pemrosesan di hilirnya, dan pembahasannya menyambung ke topik dispatcher.
Kesalahan umum#
Kesalahan pertama adalah memperlakukan aliran dingin seakan-akan hidup terus-menerus. Builder flow dieksekusi hanya selama pengumpulan berlangsung, sehingga setelah collect selesai, tidak ada lagi kode yang berjalan, dan perubahan pada sumber data sesudahnya hanya terbaca pada pengumpulan berikutnya. Pemahaman ini penting sebelum menyimpulkan bahwa aliran mengirimkan pembaruan secara otomatis tanpa dikumpulkan.
Kesalahan kedua adalah memakai satu aliran dingin sebagai saluran kejadian yang dibagikan kepada banyak penerima. Setiap penerima yang mengumpulkan akan memicu eksekusi penuh aliran secara terpisah, sehingga sumber yang mahal, misalnya pembacaan jaringan, dipanggil berulang tanpa perlu. Kebutuhan berbagi kejadian panas antar banyak penerima ditangani jenis Flow lain pada library, sedangkan builder flow tetap menjadi pilihan untuk aliran yang memproduksi nilai per permintaan.
Ringkasan#
Flowadalah aliran nilai asynchronous yang dingin, dibuat dengan builderflowyang memanggilemitper nilai, dan hanya berjalan ketika operator terminal seperticollectdipanggil.- Setiap pengumpulan mengeksekusi builder dari awal, sehingga dua
collectmenghasilkan dua eksekusi penuh yang tidak berbagi keadaan. - Operator
filterdanmapdirangkai di antara sumber dancollect, dan nilainya diproses satu per satu secara lazy sepertiSequence. flowOnmengubah dispatcher eksekusi builder tanpa memengaruhi pemrosesan di hilirnya.
Referensi#
- Flow — JetBrains — Dokumentasi resmi Flow, builder flow, cold stream, operator antara, dan operator terminal.
- Coroutines guide — JetBrains — Panduan resmi coroutines Kotlin sebagai peta seluruh topik pada modul ini.