簡體   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