在流程中实施退避策略

时间:2020-07-14 07:02:22

标签: kotlin kotlin-coroutines kotlin-flow

我正在尝试仅使用Kotlin flow来实施退避策略。

我需要从timeA到timeB取数据

result = dataBetween(timeA - timeB)

如果结果为空,那么我想使用指数补偿增加结束时间窗口

result = dataBetween(timeA - timeB + exponentialBackOffInDays)

我关注的是这篇文章,其中介绍了如何在rxjava2中实现这一目标。

但是在flow没有takeUntil运算符yet的情况下陷入困境。

您可以在下面查看我的实施情况。

fun main() {
    runBlocking {
        (0..8).asFlow()
            .flatMapConcat { input ->
                // To simulate a data source which fetches data based on a time-window start-date to end-date 
                // available with in that time frame.
                flow {
                    println("Input: $input")
                    if (input < 5) {
                        emit(emptyList<String>())
                    } else { // After emitting this once the flow should complete
                        emit(listOf("Available"))
                    }
                }.retryWhenThrow(DummyException(), predicate = {
                    it.isNotEmpty()
                })
            }.collect {
                //println(it)
            }
    }
}

class DummyException : Exception("Collected size is empty")

private inline fun <T> Flow<T>.retryWhenThrow(
    throwable: Throwable,
    crossinline predicate: suspend (T) -> Boolean
): Flow<T> {
    return flow {
        collect { value ->
            if (!predicate(value)) {
                throw throwable // informing the upstream to keep emitting since the condition is met
            }
            println("Value: $value")
            emit(value)
        }
    }.catch { e ->
        if (e::class != throwable::class) throw e
    }
}


工作正常,除非流量具有成功的值,但流量仍继续从上游流量收集到8,但理想情况下,流量应在到达5本身时停止。

任何有关如何解决此问题的帮助都会有所帮助。

1 个答案:

答案 0 :(得分:0)

也许这与您的确切设置不符,但您最好不使用collectfirst{...}来调用firstOrNull{...} 找到元素后,这将自动停止上游流。
例如:

flowOf(0,0,3,10)
    .flatMapConcat {
        println("creating list with $it elements")
        flow {
            val listWithElementCount = MutableList(it){ "" }  // just a list of n empty strings
            emit(listWithElementCount)
        }
    }.first { it.isNotEmpty() }

在旁注中,您的问题听起来像是常规的暂挂功能更合适。 像

suspend fun getFirstNonEmptyList(initialFrom: Long, initialTo: Long): List<Any> {
    var from = initialFrom
    var to = initialTo
    while (coroutineContext.isActive) {
        val elements = getElementsInRange(from, to) // your  "dataBetween"
        if (elements.isNotEmpty()) return elements
        val (newFrom, newTo) = nextBackoff(from, to)
        from = newFrom
        to = newTo
    }
    throw CancellationException()
}