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<Int> result type, means we can only return all the values at once.

2.4 Flows

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

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<Int> = (1..3).asFlow()
fun simple(): Flow<Int> = flowOf(1,2,3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin StateFlowKotlin协程库中的一种流(Flow)实现,用于在异步场景下处理状态的变化。StateFlow可用于代替LiveData或RxJava的Observable,提供了一种简洁且类型安全的方式来观察和响应状态的变化。 StateFlow是一个具有状态的流(flow),它可以发射新的值并保持最新的状态。与普通的Flow相比,StateFlow更适用于表示单一的可变状态,并且可以方便地在多个观察者之间共享。 StateFlow在使用上类似于普通的Flow,你可以使用`stateIn`函数将其转换为一个只读的SharedFlow,并使用`collect`或`conflate`等操作符来观察和处理状态的变化。 下面是一个使用StateFlow的示例代码: ``` import kotlinx.coroutines.* import kotlinx.coroutines.flow.* fun main() = runBlocking { val state = MutableStateFlow("Initial state") val job = launch { state.collect { value -> println("Received state: $value") } } state.value = "Updated state" job.cancel() } ``` 在上面的示例中,我们创建了一个MutableStateFlow对象并初始化为"Initial state"。然后使用`collect`函数来观察state的变化,并在状态发生变化时打印出新的值。我们通过修改`state.value`来更新状态,并在控制台上看到"Received state: Updated state"的输出。 总之,Kotlin StateFlow提供了一种方便的方式来处理状态的变化,并与Kotlin协程无缝集成,使得在异步场景下处理状态变化更加简洁和可靠。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值