Flow:从 Lambda 表达式讲起

188 阅读2分钟

定义一个最简单的 Lambda 表达式,并可以多次调用它:

val f: () -> Unit = {
    println("foo")
}

fun main() {
    f() // foo
    f() // foo
}

很容易可以让这个 Lambda 表达式支持挂起,添加 suspend 修饰符:

val f: suspend () -> Unit = {
    delay(1_000)
    println("foo")
}

suspend fun main() {
    f() // foo
    f() // foo
}

通过 Kotlin 高阶函数的定义,函数可以作为参数传递给其它函数。 这里,我们让 Lambda 表达式接收一个挂起函数,并将参数称为 emit :

val f: suspend (suspend (String) -> Unit) -> Unit = { emit ->
    delay(1_000)
    emit("foo")
}

suspend fun main() {
    f {
        println(it) // foo
    }
}

通常我们更习惯于通过接口来定义某种“模式”,而不是函数类型。 这里,可以定义一个函数式接口:

fun interface XFlowCollector {
    suspend fun emit(value: String)
}

// 更改后:
val f: suspend (XFlowCollector) -> Unit = {
    delay(1_000)
    it.emit("foo")
}

suspend fun main() {
    f {
        println(it) // foo
    }
}

在 it 上调用 emit 并不方便,此时可以将 XFlowCollector 作为 Receiver, 那么在函数体内,可以不再使用 “it”:

// 更改后:
val f: suspend XFlowCollector.() -> Unit = {
    delay(1_000)
    emit("foo")
}

suspend fun main() {
    f {
        println(it) // foo
    }
}

同样的,更改后的 Lambda 表达式 f,是否可以改写成接口的形式呢? 我们将接口称为 XFlow :

interface XFlow {
    suspend fun collect(collector: XFlowCollector)
}

suspend fun main() {
    val xFlow = object : XFlow {
        override suspend fun collect(collector: XFlowCollector) {
            // TODO:
        }
    }
    xFlow.collect {
        println(it)
    }
}

此时,匿名对象 xFlow 调用 collect 之后,要执行的就是 "TODO",而这个 "TODO",就是上面的 f,即:

suspend fun main() {
    val xFlow = object : XFlow {
        override suspend fun collect(collector: XFlowCollector) {
            collector.f()
        }
    }
    xFlow.collect {
        println(it) // foo
    }
}

最后,我们定义一个顶级函数,来简化这个流程:

fun xFlow(f: suspend XFlowCollector.() -> Unit) = object : XFlow {
    override suspend fun collect(collector: XFlowCollector) {
        collector.f() // 调用传入的 f
    }
}

suspend fun main() {
    val xFlow = xFlow {
        emit("bar")
    }
    xFlow.collect {
        println(it)  // bar
    }
}

最后的最后,使用泛型参数来替换String,让 xFlow 可以支持任意类型:

fun interface XFlowCollector<X> {
    suspend fun emit(value: X)
}

interface XFlow<X> {
    suspend fun collect(collector: XFlowCollector<X>)
}

fun <X> xFlow(f: suspend XFlowCollector<X>.() -> Unit) = object : XFlow<X> {
    override suspend fun collect(collector: XFlowCollector<X>) {
        collector.f()
    }
}

到此为止,以上几乎是 Flow 的实现方式!简单地说,以下为例:

suspend fun main() {
    val xFlow = xFlow {
        delay(1_000)
        emit("bar")
    }
    xFlow.collect {
        println(it)
    }
}

每当调用 collect 时,函数会执行:

{
    delay(1_000)
    emit("bar")
}

每当调用到 emit 时,函数会执行:

{
    println(it)
}

这就是 Flow 的工作原理!

特别的,如果一开始,我们不是定义函数式接口,仅定义最普通的接口,这里更能直接看出端倪:

interface XFlowCollector {
    suspend fun emit(value: String)
}
suspend fun main() {
    val xFlow = xFlow {
        delay(1_000)
        emit("bar") // 调用 emit,即是调用 “ println(it) ”
    }
    xFlow.collect(object : XFlowCollector {
        override suspend fun emit(value: String) {
            println(value)
        }
    })
}