繁体   English   中英

如何跳过流并仅发送最后发出的值

[英]How to skip a Flow and just send last emitted value

我正在开发一个 web 应用程序,以可视化基于 Websockets 的迷宫生成和求解算法。 生成算法实现接口 IMazeGenerator 并返回一个 Flow。

interface IMazeGenerator {

    fun generate(maze: Maze): Flow<Maze>

}

在生成器算法的每一步之后,都会发出更新的迷宫。 然后,收集器会在短暂延迟后将中间步骤发送给客户端。

generatorFlow
            .map { it.toDto() }
            .delay(150)
            .onEach {
                client.send(UpdateMaze(it))
            }
            .launchIn(this.scope)

现在我的问题是:我想为客户提供跳过中间步骤的可能性,然后只发送最后一个结果,即最终的迷宫。 为此,我首先需要能够确定发射器是否已经完成(即最终迷宫已经发射),如果是,则在跳过命令到来时仅发送最后一个结果。

不幸的是,我现在根本不知道,我什至不确定这是否适用于流程。 欢迎任何帮助。

您可以创建一个扩展 function 来收集流并返回最后一个元素。 请注意,如果输入流是无限的,它将无限期暂停。

像这样的东西:

suspend fun <T> Flow<T>.lastOrNull(): T? {
    var elem: T? = null
    collect { elem = it }
    return elem
}

现在客户端只需执行val finalMaze = mazeSteps.lastOrNull()即可获得最终迷宫。

如果您想将输入流转换为只发出最终迷宫的流,您可以这样做:

fun <T> Flow<T>.skipUntilLast(): Flow<T> = flow { 
    val last = lastOrNull()
    if (last != null) emit(last)
}

谢谢marstran,不幸的是这还没有直接起作用,但你给了我必要的灵感。 让我先解释问题,然后是我的解决方案。

一旦触发生成,流程就会启动。 如果客户端想在某个点之后跳过所有进一步的中间步骤,我需要从这个流中传输最后一个发射。 使用您的解决方案,问题是在这种情况下我会重新启动流程,并获得不同的结果,因为某些算法是不确定的。 另一个问题是我首先必须确定发射器是否已经计算了最后一个结果,因为某些算法确实需要一些时间。 此时,流程将是可跳过的。 因此,我还必须使用缓冲区来忽略背压。

我现在创建了一个扩展 function,它在最后一个发出的值上接收一个回调 function。

fun <T> Flow<T>.finalValueCallback(block: suspend (T?) -> Unit) = flow {
    var finalValue: T? = null
    collect {
        emit(it)
        finalValue = it
    }
    block(finalValue)
}

然后我使用这个回调来保存当前流的最后一个结果。 如果出现跳过命令,我会中断流程并仅发送最后一个结果。

generatorJob = generatorFlow
            .onStart { client.send(UpdateGeneratorState(GeneratorState.RUNNING)) }
            .map { it.toDto() }
            .finalValueCallback {
                finalMaze = it
                client.send(UpdateGeneratorState(GeneratorState.SKIPPABLE))
            }
            .buffer()
            .delay(150)
            .onEach {
                client.send(UpdateMaze(it))
            }
            .onCompletion { cause ->
                finalValue = null
                if (cause == null || cause is FlowSkippedException) {
                    client.send(UpdateGeneratorState(GeneratorState.COMPLETED))
                } else {
                    client.send(UpdateGeneratorState(GeneratorState.CANCELLED))
                }
            }
            .launchIn(this.scope)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM