如何创建一个适用于Rx列表的计时器?

时间:2018-06-06 18:14:03

标签: rx-java2 rx-kotlin2

我想在完成之前查找要找到的整个项目列表,如果找不到整个列表,则抛出异常(超时或自定义)。就像内置的Observable.timer()一样,但是一旦发出第一个项目而不是测试传递,我希望它能够找到列表中的所有项目。

这是一个例子。假设我有一些测试函数可以发出Observable< FoundNumber>。它看起来像这样:

var emittedList: List<String?> = listOf(null, "202", "302", "400")

data class FoundNumber(val numberId: String?)

fun scanNumbers(): Observable<FoundNumber> = Observable
    .intervalRange(0, 
                   emittedList.size.toLong(), 
                   0, 
                   1, 
                   TimeUnit.SECONDS).map { index -> 
                     FoundNumber(emittedList[index.toInt()]) }

然后将调用该函数以获取将与预期数字列表进行比较的数字。如果scanForNumbers中有其他数字不在“目标”列表中,则无关紧要。他们将被忽略。像这样:

val expectedNumbers = listOf("202", "302","999")

        scanForNumbers(expectedNumbers)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribeOn(Schedulers.io())
                .subscribe { value -> Log.d(TAG, "Was returned a $value") }

因此,预期数字(202,302和999)与将要发射的数字(202,302和400)不完全匹配。因此,应该发生超时,但是使用Observable.timer()的内置版本,由于至少观察到一个项目,因此不会超时。

这是我想要的。任何人都知道如何在RxJava / RxKotlin中编写代码吗?

fun scanForNumbers(targets: List<String>): Observable<FoundNumber> {
  val accumulator: Pair<Set<Any>, FoundNumber?> = targets.toSet() to null
    return scanNumbers()
        .SPECIAL_TIMEOUT_FOR_LIST(5, TimeUnit.SECONDS, List)
        .scan(accumulator) { acc, next ->
            val (set, previous) = acc
            val stringSet:MutableSet<String> = hashSetOf()

            set.forEach { stringSet.add(it.toString()) }

            val item = if (next.numberId in stringSet) {
                next
            } else null
            (set - next) to item       // return set and nullable item
        }
        .filter { Log.d(TAG, "Filtering on ${it.second}")
                  it.second != null }  // item not null
        .take(targets.size.toLong())         // limit to the number of items
        .map { it.second }                   // unwrap the item from the pair
        .map { FoundController(it.numberId) }  // wrap in your class
}

你如何编码,希望使用RxJava / Kotlin,如上所述在列表上超时的方法?

1 个答案:

答案 0 :(得分:0)

我想我现在知道了,你希望超时从你订阅的那一刻开始计算,而不是在你观察物品之后。

如果这是您所需要的,那么takeUntil运营商可以帮助您:

return scanNumbers()
    .takeUntil(Observable.timer(5, TimeUnit.SECONDS))
    .scan(accumulator) { acc, next -> ...

在这种情况下,计时器将在您订阅后立即开始计时。如果主要观察者在那之前完成,那么很好,如果没有,那么计时器将完成主要的观察结果。

但是takeUntil本身不会抛出错误,它只会完成。如果您需要以错误结束,那么您可以使用以下组合:

return scanNumbers()
    .takeUntil(
            Observable
                    .error<Void>(new TimeoutError("timeout!"))
                    .delay(5, TimeUnit.SECONDS, true))
    .scan(accumulator) { acc, next -> ...