Intermediate flow operators: 如何在转换后的值相同时不发出?

huangapple go评论62阅读模式
英文:

Intermediate flow operators: How to not emit if the transformed value is the same?

问题

以下是您提供的代码的翻译部分:

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking{
    val startTime = System.currentTimeMillis()
    val elapsedTime = MutableStateFlow(0L)
    // 启动秒表
    launch {
        while(true){
            elapsedTime.value = System.currentTimeMillis() - startTime
            delay(100)
        }
    }

    // 更新UI
    launch {
        elapsedTime.map{
            it/1000
        }.collect{
            println("$it 秒")
        }
    }
}
0000000000111
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking{
    val startTime = System.currentTimeMillis()
    val elapsedTime = MutableStateFlow(0L)
    // 启动秒表
    launch {
        while(true){
            elapsedTime.value = (System.currentTimeMillis() - startTime)/1000
            delay(5)
        }
    }

    // 更新UI
    launch {
        elapsedTime.collect{
            println("$it 秒")
        }
    }
}
0123

您想要从一个根流elapsedTime.value = System.currentTimeMillis() - startTime中创建多个观察者,并使用.map{it/a}来实现不同精度级别的观察。这是一个可行的方法,您已经在第一个示例中成功实现了这一点。通过在根流上应用.map操作,您可以派生出具有不同精度的多个观察者。在第一个示例中,您将毫秒转换为秒以获得更低的精度。

如果您有任何其他问题或需要进一步的帮助,请随时提问。

英文:

I am trying to code a stopwatch using flows.

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking{
    val startTime = System.currentTimeMillis()
    val elapsedTime = MutableStateFlow(0L)
    // start stopwatch
    launch {
        while(true){
            elapsedTime.value = System.currentTimeMillis() - startTime
            delay(100)
        }
    }

    // update ui
    launch {
        elapsedTime.map{
            it/1000
        }.collect{
            println("$it seconds")
        }
    }
}
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
0 seconds
1 seconds
1 seconds
1 seconds

The problem is that intermediate flows do not keep track of whether the value changed like the following code:

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

fun main(): Unit = runBlocking{
    val startTime = System.currentTimeMillis()
    val elapsedTime = MutableStateFlow(0L)
    // start stopwatch
    launch {
        while(true){
            elapsedTime.value = (System.currentTimeMillis() - startTime)/1000
            delay(5)
        }
    }

    // update ui
    launch {
        elapsedTime.collect{
            println("$it seconds")
        }
    }
}
0 seconds
1 seconds
2 seconds
3 seconds

I want to have a single root flow elapsedTime.value = System.currentTimeMillis() - startTime from which I can have multiple observers with different levels of accuracy using .map{it/a}. Is there a clean way to accomplish this?

答案1

得分: 1

是的,使用 distinctUntilChanged()。仅作为创建流的另一种方式的示例,而无需使用 MutableStateFlow,我使用了 flow 构建器和 stateIn。以及一种备用(我更喜欢的)方式,使用 onEach/launchIn 来在自己的协程中收集流,而不是使用 collect/launch,以减少代码缩进和嵌套括号。

fun main(): Unit = runBlocking {
    val elapsedTime = flow {
        val startTime = System.currentTimeMillis()
        while (true) {
            emit(System.currentTimeMillis() - startTime)
            delay(5)
        }
    }.stateIn(this, SharingStarted.Eagerly, 0L)

    // 更新界面
    elapsedTime.map { it / 1000L }
        .distinctUntilChanged()
        .onEach { println("$it 秒") }
        .launchIn(this)

    launch {
        elapsedTime.map { it / 100L }
            .distinctUntilChanged()
            .collect { println("$it 十分之一秒") }
    }
}
英文:

Yes, using distinctUntilChanged(). And just as an example of different way to create the flow without having to use MutableStateFlow, I used the flow builder with stateIn. And an alternate (my preferred) way to collect a flow in its own coroutine using onEach/launchIn instead of collect/launch for less code indentation and nested brackets.

fun main(): Unit = runBlocking {
    val elapsedTime = flow {
        val startTime = System.currentTimeMillis()
        while(true){
            emit(System.currentTimeMillis() - startTime)
            delay(5)
        }
    }.stateIn(this, SharingStarted.Eagerly, 0L)

    // update ui
    elapsedTime.map { it / 1000L }
        .distinctUntilChanged()
        .onEach { println("$it seconds") }
        .launchIn(this)

    launch {
        elapsedTime.map { it / 100 L }
            .distinctUntilChanged()
            .collect { println("$it deciseconds") }
    }
}

</details>



huangapple
  • 本文由 发表于 2023年6月5日 04:24:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76402270.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定