每个发射的映射-SwitchMap是否保证至少有1个发射/ ConcatMap混合?

时间:2018-12-16 05:02:57

标签: rxjs rx-java reactive-programming rx-swift reactive

让我为如何在RX中做到这一点而烦恼。

T 实际用例是将LowerLevelEvent(val userId:String)映射到HigherLevelEvent(val user:User),其中Observable提供了User,因此它可以发出n次,输出

LowerLevelEvent1(abc) -> HigherLevelEvent1(userAbc(nameVariation1)
LowerLevelEvent2(abc) -> HigherLevelEvent2(userAbc(nameVariation1)
LowerLevelEvent3(abc) -> HigherLevelEvent3(userAbc(nameVariation1)
LowerLevelEvent4(abc) -> HigherLevelEvent4(userAbc(nameVariation1)
                         HigherLevelEvent4(userAbc(nameVariation2)
                         HigherLevelEvent4(userAbc(nameVariation3)

所以我的幼稚解决方案是使用CombineLatest。因此,在userId不变的情况下,订阅了observable的用户,即在新的lowerLevelEmits及其userId不变的情况下不重新订阅

val _lowerLevelEventObservable: Observable<LowerLevelEvent> = lowerLevelEventObservable
                        .replayingShare()

val _higherLevelEventObservable: Observable<HigherLevelEvent> = Observables
                        .combineLatest(
                _lowerLevelEventObservable, 
                _lowerLevelEventObservable
                            .map { it.userId }
                            .distinctUntilChanged()
                            .switchMap { userRepository.findByIdObservable(it)
            ) { lowerLevelEvent, user -> createHigherLevelInstance... }

但是这有一个小问题,因为CombineLatest中的两个来源都来自相同的可观察对象。

然后我想到了

lowerLevelObservable.
   .switchMap { lowerLevelEvent ->
      userRepository.findByIdObservable(lowerLevelEvent.userId)
          .map { user -> createHigherLevelInstance... }
   }

但是,如果lowerLevelObservable快速发出,则此操作可能会中断,并且由于用户可观察项可能需要一些时间,因此可以跳过给定的lowerLevelX事​​件,而我不能这样做。此外,它还会重新订阅用户可观察到的每次发射,这很浪费,因为它极不可能发生变化

那么,也许concatMap?那个问题是用户可观察的对象没有完成,所以concatMap无法工作。

有人知道吗?

非常感谢

//澄清: 基本上是将A变体(A1,A2 ..)映射到A'变体(A1',A2'..)的映射,同时将查询对象附加到该对象,在该对象中是可观察到的查询,因此在进行映射后可能会重新查询AX'需要与新的查询结果一起发布。但是查询很冷,无法完成

例如A1(1) -> A1'(user1), A2(1) -> A2'(user1), A3(1) -> A3'(user1)的示例-现在有人在应用程序的其他位置更改了user1,因此下一个发出的是A3'(user1')

1 个答案:

答案 0 :(得分:1)

根据您的评论,以下内容将在RxSwift中运行。我不知道如何将其转换为RxJava。不过,老实说,我认为这里存在对Rx的根本滥用。祝你好运。

工作原理:如果允许订阅,它将进行订阅,否则它将事件添加到缓冲区中以备后用。如果当前未订阅内部事件,或者当前已订阅的内部Observable发出了元素,则允许订阅。

警告:它不能正确处理当前的完成情况。我会把它留给您作为练习。

func example(lowerLevelEventObservable: Observable<LowerLevelEvent>, userRepository: UserRepository) {
    let higherLevelEventObservable = lowerLevelEventObservable
        .flatMapAtLeastOnce { event in // RxSwift's switchLatest I think.
            Observable.combineLatest(
                Observable.just(event),
                userRepository.findByIdObservable(event.userId),
                resultSelector: { (lowLevelEvent: $0, user: $1) }
            )
        }
        .map { createHigherLevelInstance($0.lowLevelEvent, $0.user) }

    // use higherLevelEventObservable
}

extension ObservableType {
    func flatMapAtLeastOnce<U>(from fn: @escaping (E) -> Observable<U>) -> Observable<U> {
        return Observable.create { observer in
            let disposables = CompositeDisposable()
            var nexts: [E] = []
            var disposeKey: CompositeDisposable.DisposeKey?
            var isAllowedToSubscribe = true
            let lock = NSRecursiveLock()
            func nextSubscription() {
                isAllowedToSubscribe = true
                if !nexts.isEmpty {
                    let e = nexts[0]
                    nexts.remove(at: 0)
                    subscribeToInner(e)
                }
            }

            func subscribeToInner(_ element: E) {
                isAllowedToSubscribe = false
                if let key = disposeKey {
                    disposables.remove(for: key)
                }
                let disposable = fn(element).subscribe { innerEvent in
                    lock.lock(); defer { lock.unlock() }
                    switch innerEvent {
                    case .next:
                        observer.on(innerEvent)
                        nextSubscription()
                    case .error:
                        observer.on(innerEvent)
                    case .completed:
                        nextSubscription()
                    }
                }
                disposeKey = disposables.insert(disposable)
            }

            let disposable = self.subscribe { event in
                lock.lock(); defer { lock.unlock() }
                switch event {
                case let .next(element):
                    if isAllowedToSubscribe == true {
                        subscribeToInner(element)
                    }
                    else {
                        nexts.append(element)
                    }
                case let .error(error):
                    observer.onError(error)
                case .completed:
                    observer.onCompleted()
                }
            }
            _ = disposables.insert(disposable)
            return disposables
        }
    }
}