Kotlin Asynchronous Flow

Kotlin Flow document

1. Asynchronous Flow

A suspending function asynchronously returns a single value, but how can we return multiple asynchronously computed values? This is where Kotlin Flows come in.

2. Representing multiple values

2.1 Collections

Multiple values can be represented in Kotlin using collections. For example, we can have a simple function that returns a List of three numbers and then print them all using forEach:

fun main(args: Array<String>) {
    simple().forEach { value -> println(value) }
}

fun simple() : List<Int> = listOf(1, 2, 3)

可以返回多个值但是不是异步的。

2.2 Sequences

If we are computing the numbers with some CPU-consuming blocking code (each computation taking 100ms), then we can represent the numbers using a Sequence

fun main(args: Array<String>) {
    simple().forEach { value -> println(value) }
    println("----------------print over !------------")
}

fun simple() : Sequence<Int> = sequence { // sequence builder
    for (i in 1..3) {
        Thread.sleep(100) // pretend we are computing it
        yield(i) // yield next value
    }
}

1
2
3
----------------print over !------------

However, this computation blocks the main thread that is running the code.

2.3 Suspending functions

When these values are computed by asynchronous code we can mark the simple function with a suspend modifier, so that it can perform its work without blocking and return the result as a list:

fun main(args: Array<String>) = runBlocking {
    simple().forEach { value -> println(value) }
    println("----------------print over !------------")
}

suspend fun simple() : List<Int> {
    delay(1000) // do computation
    return listOf(1,2,3)
}

这种方式是异步的,并且不会阻塞主线程,但是所有值是打包一起返回。Using the List result type, means we can only return all the values at once.

2.4 Flows

为了表示异步计算的数据流,使用Flow类型。

fun main(args: Array<String>) = runBlocking {
   launch {
       for (k in 1..3) {
           println("I'm not blocked $k")
           delay(100)
       }
   }
    simple().collect {value ->
        println(value)
    } //这里挂起runBlocking
    println("----------------print over !------------")
}

fun simple(): Flow<Int>  = flow {
    for (i in 1 .. 3) {
        delay(100)
        emit(i)
    }
}

I'm not blocked 1
1
I'm not blocked 2
2
I'm not blocked 3
3
----------------print over !------------

This code waits 100ms before printing each number without blocking the main thread. This is verified by printing “I’m not blocked” every 100ms from a separate coroutine that is running in the main thread.

Flow代码和之前的代码有以下不同之处

  1. Flow类型的构造函数称为flow.
  2. flow{...}中的代码构建块可以挂起。
  3. simple()函数不在是一个挂起函数
  4. 使用emit函数从流中发出值。
  5. 使用collect函数从流收集值。

如果将simple's flow { ... }代码中的delay替换为Thread.sleep,主线程将阻塞

fun simple(): Flow<Int>  = flow {
    for (i in 1 .. 3) {
        Thread.sleep(100)
        emit(i)
    }
}

1
2
3
----------------print over !------------
I'm not blocked 1
I'm not blocked 2
I'm not blocked 3

flow传入的挂起函数

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

2. Flows are cold

·Flows are cold streams similar to sequences — the code inside a flow builder does not run until the flow is collected. This becomes clear in the following example:


fun main() = runBlocking<Unit> {
    println("Calling simple function...")
    val flow = simple()
    println("Calling collect...")
    flow.collect { value -> println(value) }
    println("Calling collect again...")
    flow.collect { value -> println(value) }
}

fun simple(): Flow<Int> = flow {
    println("Flow started")
    for (i in 1..3) {
        delay(100)
        emit(i)
    }
}
Calling simple function...
Calling collect...
Flow started
1
2
3
Calling collect again...
Flow started
1
2
3

这是simple()函数(which returns a flow)没有使用suspend修饰符标记的关键原因,simple()调用本身返回很快,不等待任何东西。每次收集时,流都会重新启动,这就是为什么每次再次调用collect时我们都会看到“flow started”

3. Flow cancellation basics

lows adhere to the general cooperative cancellation of coroutines. As usual, flow collection can be cancelled when the flow is suspended in a cancellable suspending function (like delay). The following example shows how the flow gets cancelled on a timeout when running in a withTimeoutOrNull block and stops executing its code.

fun simple(): Flow<Int> = flow {
    for (i in 1..3) {
        delay(100)
        println("Emitting $i")
        emit(i)
    }
}

fun main(): Unit = runBlocking {
    withTimeoutOrNull(250) {
        simple().collect {value -> println(value) }
    }
    println("Done")
}
Emitting 1
1
Emitting 2
2
Done

withTimeoutOrNull是一个挂起函数,当超时后被取消,里面运行的流也会被取消。

public suspend fun <T> withTimeoutOrNull(timeMillis: Long, block: suspend CoroutineScope.() -> T): T? {
    if (timeMillis <= 0L) return null

    var coroutine: TimeoutCoroutine<T?, T?>? = null
    try {
        return suspendCoroutineUninterceptedOrReturn { uCont ->
            val timeoutCoroutine = TimeoutCoroutine(timeMillis, uCont)
            coroutine = timeoutCoroutine
            setupTimeout<T?, T?>(timeoutCoroutine, block)
        }
    } catch (e: TimeoutCancellationException) {
        // Return null if it's our exception, otherwise propagate it upstream (e.g. in case of nested withTimeouts)
        if (e.coroutine === coroutine) {
            return null
        }
        throw e
    }
}

4. Flow builders

流的构造器,常见的就是flow{ ... },另外还有:

  1. The flowOf builder defines a flow that emits a fixed set of values.
  2. Various collections and sequences can be converted to flows using the .asFlow() extension function.

For example:two builders as floow are same.

fun simple(): Flow = (1..3).asFlow()
fun simple(): Flow = flowOf(1,2,3)

你可能感兴趣的:(kotlin,java,开发语言)