Flow 是什麼#

Flow 是 Kotlin Coroutine 用來處理「非同步資料流」的工具。它會按照發射(emit)的順序依次送出資料,最後正常結束或拋出例外。

官方文件描述:An asynchronous data stream that sequentially emits values and completes normally or with an exception.

把資料透過 Flow 發送出去之後,這些資料在被「接收」之前不會真的執行運算。我們甚至可以在接收前先用一些函式把資料轉成需要的形狀。

Flow 與 Channel 的差異#

兩者都能傳遞資料,但定位不同:

  • Channel 一次取出一個值,需要呼叫多次 receive
  • Flow 取出的是整條資料流(Stream),用一次 collect 就能處理整條流上的所有元素。

換個說法:

  • 用 Channel 時,每筆資料對應一組 sendreceive
  • 用 Flow 時,發射端用 emit,接收端用 collect 一次處理整串。

第一個 Flow 範例#

Kotlin 提供多種 flow 建構器(Flow Builder),這裡先用 flow { ... }

fun flow1(): Flow<Int> = flow {
    println("Flow started")
    repeat(10) {
        delay(100)
        emit(it)
    }
}

幾個值得注意的點:

  • 回傳 Flow<Int> 的函式本身不需要是 suspend 函式。原因是 Flow 在被 collect 之前不會執行內部程式碼,真正會 suspend 的是接收端。
  • flow { ... } 的 lambda 本身是 suspend 的(所以可以呼叫 delay)。

builder 的簽名如下:

public fun <T> flow(@BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> = SafeFlow(block)

emit 其實是 FlowCollector 的成員函式:

public interface FlowCollector<in T> {
    public suspend fun emit(value: T)
}

接收 Flow 的資料#

Flow<T> 介面只有一個核心方法:

public interface Flow<out T> {

    @InternalCoroutinesApi
    public suspend fun collect(collector: FlowCollector<T>)
}

也就是說,Flow 的設計可以總結為:

  • emit 把值放進 stream。
  • collect 把值取出來。

接收範例:

fun main() = runBlocking {
    val flow = flow1()
    flow.collect { value -> println(value) }
}

執行結果:

Flow started
0
1
2
3
4
5
6
7
8
9

小結#

  • Flow 是非同步處理資料流的方式,發射用 emit、接收用 collect
  • 建立 flow 的函式不需要是 suspend,因為內部的程式碼要等到 collect 時才會執行。
  • 跟 Channel 比起來,Flow 處理的是整串資料,搭配後續介紹的中間運算子使用會非常順手。
  • 下一章我們會再多看一個 Flow 的重要特性:它是 Cold Stream。

原文出處#

  • 原書/iThome:https://ithelp.ithome.com.tw/articles/10269815