RxSwift-执行控制流会导致执行两次异步操作

RxSwift-执行控制流会导致执行两次异步操作,swift,macos,rx-swift,reactivex,Swift,Macos,Rx Swift,Reactivex,看看这个例子: func query2() -> Observable<Int> { print("query2() called") return Observable.create { observer in print("creating query2() thread") let thread = Thread.init(block: { sleep(1)

看看这个例子:

func query2() -> Observable<Int> {
    print("query2() called")
    return Observable.create { observer in
        print("creating query2() thread")
        let thread = Thread.init(block: {
            sleep(1)
            let numbers = [
                1,2,3,4,5
            ]
            for num in numbers {
                observer.onNext(num)
            }
            observer.onCompleted()
        })
        thread.start()
        return Disposables.create {
            thread.cancel()
        }
    }
}

let numbers = query2()
let even = numbers.filter { $0 % 2 == 0 }
let odd = numbers.filter { $0 % 2 != 0 }
let merged = even.concat(odd)

merged.subscribe(onNext: { n in
    print(n)
})
但是,当从
odd
中提取值时,线程似乎是第二次创建的

实际产量:

query2() called
creating query2() thread
2
4
creating query2() thread
1
3
5
我看了看这段代码,然后想-啊,我错过了
.share()
操作符,因为
偶数和
奇数都来自同一个流。我最初没有添加这一点,因为我最终将它们合并到一个要订阅的流
merged
,并认为Rx会为我做优化

所以我使用了share():
let numbers=query2().share()

产量仍然保持不变


如何防止这种情况发生?

每次订阅生成的可观察对象时,都会调用传递给
可观察对象的闭包。create

share()
运算符具有引用计数。它将在收到订阅请求时订阅其源,然后如果在源运行时收到另一个请求,它也将向第二个源发送事件

令人惊讶的是,您的observable在第一个subscribe返回之前完成,因此没有什么可共享的。请注意,当您在后台线程内调用
onNext
时,它会立即调用传递给该后台线程内subscribe的闭包。当您调用
onCompleted
时,它会立即完成。它不会等待订阅退出来完成这些事情

这里的解决方案是使用多播操作符使您的
数字
可见。将ReplaySubject传递给它,它将存储输出并将其重播给任何后续订阅者。不要忘记调用
connect()
,以使可观察对象运行其生成器函数

大概是这样的:

let numbers = Observable.deferred { () -> Observable<Int> in
    print("called")
    return Observable.from([1, 2, 3, 4, 5, 6])
}
.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .default)) // this ensures that the block passed to `deferred` is called on a background thread.
.multicast(ReplaySubject<Int>.createUnbounded())

numbers.connect()

let even = numbers.filter { $0 % 2 == 0 }
let odd = numbers.filter { $0 % 2 != 0 }
let merged = even.concat(odd)

merged.subscribe(onNext: { print($0) })
let numbers=Observable.deferred{()->Observable in
印刷品(“被称为”)
可观察的返回。从([1,2,3,4,5,6])
}
.subscribe(on:ConcurrentDispatchQueueScheduler(qos:.default))//这确保在后台线程上调用传递给'deferred'的块。
.multicast(ReplaySubject.createUnbounded())
数字连接()
设偶数=numbers.filter{$0%2==0}
设奇数=numbers.filter{$0%2!=0}
让合并=偶数.concat(奇数)
合并。订阅(onNext:{print($0)})

效果很好。似乎如果我要在Rx世界中完成大部分编程,我需要做很多多播(这需要我非常清楚代码图,因为这是明确的)。您是否建议在订阅后执行所有同步转换,尽快进入非Rx世界?到目前为止,我已经使用RxSwift 6年了。我从来没有需要在生产应用程序或示例应用程序中使用多播。
share
操作符是我所需要的全部(有时需要重播)。这里的特殊情况是,您正在吐出流,然后使用concat重新组合它,从而在时间上错开它的订阅。这是一件非常不寻常的事情。
let numbers = Observable.deferred { () -> Observable<Int> in
    print("called")
    return Observable.from([1, 2, 3, 4, 5, 6])
}
.subscribe(on: ConcurrentDispatchQueueScheduler(qos: .default)) // this ensures that the block passed to `deferred` is called on a background thread.
.multicast(ReplaySubject<Int>.createUnbounded())

numbers.connect()

let even = numbers.filter { $0 % 2 == 0 }
let odd = numbers.filter { $0 % 2 != 0 }
let merged = even.concat(odd)

merged.subscribe(onNext: { print($0) })