RxObservable重复自身直到找到预期值

时间:2018-05-17 22:52:15

标签: rx-java2 rx-kotlin2

此函数的目标是创建一个定期发出值的流,直到遇到与谓词匹配的值。

以下是我提出的一些框架代码:

class Watcher<T : Any>(
        /**
         * Emits the data associated with the provided id
         */
        private val callable: (id: String) -> T,
        /**
         * Checks if the provided value marks the observable as complete
         */
        private val predicate: (id: String, value: T) -> Boolean
) {

    private val watchPool: MutableMap<String, Observable<T>> = ConcurrentHashMap()

    fun watch(id: String): Observable<T> {
        // reuse obesrvable if exists
        val existing = watchPool[id]
        if (existing != null)
            return existing
        val value = callable(id)
        if (predicate(id, value)) return Observable.just(value)
        // create new observable to fetch until complete,
        // then remove from the map once complete
        val observable = Observable.fromCallable<T> {
            callable(id)
        }.repeatWhen { /* What to put here? */ }.doOnComplete {
            watchPool.remove(id)
        }.distinctUntilChanged()
        watchPool[id] = observable
        return observable
    }

}

例如,如果我有以下枚举:

enum class Stage {
    CREATED, PROCESSING, DELIVERING, FINISHED
}

一些可调用将检索正确的阶段,我应该能够传递callable和谓词检查stage == FINISHED,并进行轮询直到我得到FINISHED事件。

我遇到的问题是,当收到的事件不是最终事件时,会产生一个观察。在这种情况下,observable应继续轮询事件,直到它收到与谓词匹配的事件或者直到它没有更多订阅者为止。

这个观察应该:

  • 在收到至少一个订阅者之前不进行轮询
  • 每x秒轮询一次
  • 如果predicate返回true,则将自己标记为完成
  • 如果它从> 0个订阅者变为0个订阅者
  • ,则自行完成

监视池的使用只是为了确保观看相同id的两个线程不会轮询两次。从地图中删除可观察量也是因为它不会堆积。出于同样的原因,不存储仅发出一个变量的可观察量以供参考。

如何为上面添加的点添加功能? 我将链接到一个我认为有用的现有RxJava Github issue,但据我所知,它不允许谓词处理可调用的值。

1 个答案:

答案 0 :(得分:1)

我最后只使用takeUntil,并使用观察的间隔方法进行轮询。

abstract class RxWatcher<in T : Any, V : Any> {

    /**
     * Emits the data associated with the provided id
     * At a reasonable point, emissions should return a value that returns true with [isCompleted]
     * This method should be thread safe, and the output should not depend on the number of times this method is called
     */
    abstract fun emit(id: T): V

    /**
     * Checks if the provided value marks the observable as complete
     * Must be thread safe
     */
    abstract fun isCompleted(id: T, value: V): Boolean

    /**
     * Polling interval in ms
     */
    open val pollingInterval: Long = 1000

    /**
     * Duration between events in ms for which the observable should time out
     * If this is less than or equal to [pollingInterval], it will be ignored
     */
    open val timeoutDuration: Long = 5 * 60 * 1000

    private val watchPool: MutableMap<T, Observable<V>> = ConcurrentHashMap()

    /**
     * Returns an observable that will emit items every [pollingInterval] ms until it [isCompleted]
     *
     * The observable will be reused if there is polling, so the frequency remains constant regardless of the number of
     * subscribers
     */
    fun watch(id: T): Observable<V> {
        // reuse observable if exists
        val existing = watchPool[id]
        if (existing != null)
            return existing
        val value = emit(id)
        if (isCompleted(id, value)) return Observable.just(value)
        // create new observable to fetch until complete,
        // then remove from the map once complete
        val observable = Observable.interval(pollingInterval, TimeUnit.MILLISECONDS, Schedulers.io()).map {
            emit(id)
        }.takeUntil {
            isCompleted(id, it)
        }.doOnComplete {
            watchPool.remove(id)
        }.distinctUntilChanged().run {
            if (timeoutDuration > pollingInterval) timeout(timeoutDuration, TimeUnit.MILLISECONDS)
            else this
        }
        watchPool[id] = observable
        return observable
    }

    /**
     * Clears the observables from the watch pool
     * Note that existing subscribers will not be affected
     */
    fun clear() {
        watchPool.clear()
    }

}