concatMap / flatMap应该在同一个调度程序

时间:2017-09-28 18:55:49

标签: reactive-programming rx-swift

给定一个Service对象,我想确保对服务的每个函数调用都不会产生副作用。在我的情况下,无论A正在做什么,除非调度程序可用,否则不会在函数B中执行任何操作。

这是这样的:

class Service {

    func handleJobA(input: String) -> Observable<String> {
        return Observable.just(input)
            .do(onNext: { (str) in
                print ("Job A: \(str)")
            })
            .concatMap { input -> Observable<String> in
                return Observable.just("Job AA: \(input)")
                    .delay(2, scheduler: self.scheduler)
                    .do(onNext: { (str) in
                        print (str)
                    })
            }

            .subscribeOn(scheduler)
    }

    func handleJobB(input: String) -> Observable<String> {
        return Observable.just(input)
            .do(onNext: { (str) in
                print ("Job B: \(str)")
            })
            .delay(1, scheduler: scheduler)
            .concatMap { input -> Observable<String> in
                return Observable.just("Job BB: \(input)")
                    .do(onNext: { (str) in
                        print (str)
                    })
            }

            .subscribeOn(scheduler)
    }


    let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "Service")
}


let service = Service()

_ = Observable.from(["1","2","3"])
    .concatMap { service.handleJobA(input: $0) }
    .subscribe(onNext:{
        print($0 + " √")
    })

_ = Observable.from(["1","2","3"])
    .concatMap { service.handleJobB(input: $0) }
    .subscribe(onNext:{
        print($0 + " √")
    })

import PlaygroundSupport

PlaygroundPage.current.needsIndefiniteExecution = true

目前,输出为:

Job A: 1
Job B: 1
Job BB: 1
Job BB: 1 √
Job B: 2
Job AA: 1
Job AA: 1 √
Job A: 2
Job BB: 2
Job BB: 2 √
Job B: 3
Job BB: 3
Job BB: 3 √
Job AA: 2
Job AA: 2 √
Job A: 3
Job AA: 3
Job AA: 3 √

然而,这显示了根本问题。内部延迟(可能发生在任何事情上,实际上是网络,处理)导致可观察的处理脱离“秩序”。

我想要的是:

Job A: 1
Job AA: 1
Job AA: 1 √
Job B: 1
Job BB: 1
Job BB: 1 √
Job B: 2
Job BB: 2
Job BB: 2 √
Job B: 3
Job BB: 3
Job BB: 3 √
Job A: 2
Job AA: 2
Job AA: 2 √
Job A: 3
Job AA: 3
Job AA: 3 √

这意味着,一旦某个功能开始处理任务,除非完成,否则没有其他人可以访问。

我之前收到了非常好的answer。它并不完全适用,因为flatMap / concatMap(?)似乎都不喜欢调度程序。

我的理论是concatMap调用确实做了正确的工作,但随后将子序列遗漏调度到调度程序队列的末尾,而我希望它在前面,接下来要处理。

1 个答案:

答案 0 :(得分:2)

我无法解释调度程序的行为......但我可以提出一个小提议

  

...一旦某个功能开始处理任务,其他任何人都无法获得   访问,除非它完成...

您可以通过handleJob传递所有concatMap来电,以获得您需要的行为:

Observable
    .from([1,2,3,4,5,6])
    .flatMap({ (value) -> Observable<String> in
        switch value % 2 == 0 {
        case true:
            return service.handleJobA(input: "\(value)")
        case false:
            return service.handleJobB(input: "\(value)")
        }
    })
    .subscribe(onNext:{
        print($0 + " √")
    })

服务类示例:

private class Service {

    private lazy var result = PublishSubject<(index: Int, result: String)>()
    private lazy var publish = PublishSubject<(index: Int, input: String, transformation: (String) -> String)>()
    private lazy var index: Int = 0
    private lazy var disposeBag = DisposeBag()

    init() {
        publish
            .asObservable()
            .concatMap({ (index, input, transformation) -> Observable<(index: Int, result: String)> in
                let dueTime = RxTimeInterval(arc4random_uniform(3) + 1)
                return Observable
                    .just((index: index, result: transformation(input)))
                    .delay(dueTime, scheduler: self.scheduler)
            })
            .bind(to: result)
            .disposed(by: disposeBag)
    }

    func handleJobA(input: String) -> Observable<String> {
        let transformation: (String) -> String = { string in
            return "Job A: \(string)"
        }
        return handleJob(input: input, transformation: transformation)
    }

    func handleJobB(input: String) -> Observable<String> {
        let transformation: (String) -> String = { string in
            return "Job B: \(string)"
        }
        return handleJob(input: input, transformation: transformation)
    }

    func handleJob(input: String, transformation: @escaping (String) -> String) -> Observable<String> {
        index += 1
        defer {
            publish.onNext((index, input, transformation))
        }
        return result
            .filter({ [expected = index] (index, result) -> Bool in
                return expected == index
            })
            .map({ $0.result })
            .take(1)
            .shareReplayLatestWhileConnected()
    }

    let scheduler = SerialDispatchQueueScheduler(internalSerialQueueName: "Service")
}