RxSwift 共享订阅执行顺序

Posted

技术标签:

【中文标题】RxSwift 共享订阅执行顺序【英文标题】:RxSwift Shared Subscription Execution Order 【发布时间】:2017-05-26 14:53:57 【问题描述】:

如何确保Observable 的订阅者将在另一个订阅者之后收到onNext 事件?

我的例子如下:

let firstNameObservable = firstName.asObservable().shareReplay(1)
let lastNameObservable = lastName.asObservable().shareReplay(1)
let bioObservable = bio.asObservable().shareReplay(1)
let websiteObservable = website.asObservable().shareReplay(1)

firstNameObservable.subscribe(onNext:  [unowned self] firstName in self.accountDetails.firstName = firstName ).disposed(by: disposeBag)
lastNameObservable.subscribe(onNext:  [unowned self] lastName in self.accountDetails.lastName = lastName ).disposed(by: disposeBag)
bioObservable.subscribe(onNext:  [unowned self] bio in self.accountDetails.bio = bio ).disposed(by: disposeBag)
websiteObservable.subscribe(onNext:  [unowned self] website in self.accountDetails.website = website ).disposed(by: disposeBag)

Observable.combineLatest(firstNameObservable, lastNameObservable, bioObservable, websiteObservable)
     [unowned self] _, _, _, _ in return self.accountDetails.validForSignUp 
    .bind(to: isValid)
    .disposed(by: disposeBag)

我希望最后一个 combineLatest 绑定在它上面的 4 个 subscriptions 中的任何一个已经执行之后触发。这是因为只有在设置了accountDetails 上的属性之后,validForSignUp 才会准确。

我知道解决此问题的方法是将validForSignUp 设为Variable 并观察它,但我们假设这是不可能的。

【问题讨论】:

isValidVariable 吗?如果可能,您应该避免使用变量和主题。 【参考方案1】:

我会做更多这样的事情:

let accountDetails = AccountDetails(firstName: firstName.orEmpty.asObserable(), lastName: lastName.orEmpty.asObservable(), bio: bio.orEmpty.asObservable(), website: website.orEmpty.asObservable())

accountDetails.validForSignup
    .bind(to: isValid)
    .disposed(by: bag)

struct AccountDetails 

    let validForSignup: Observable<Bool>

    init(firstName: Observable<String>, lastName: Observable<String>, bio: Observable<String>, website: Observable<String>) 

        let firstNameValid = firstName.map  $0.isValidFirstName 
        let lastNameValid = lastName.map  $0.isValidLastName 
        let bioValid = bio.map  $0.isValidBio 
        let websiteValid = website.map  $0.isValidWebsite 

        validForSignup = Observable.combineLatest([firstNameValid, lastNameValid, bioValid, websiteValid])  $0.allTrue() 
    


extension String 
    var isValidFirstName: Bool 
        return !isEmpty // put approprate code here
    

    var isValidLastName: Bool 
        return !isEmpty // put approprate code here
    

    var isValidBio: Bool 
        return !isEmpty // put approprate code here
    

    var isValidWebsite: Bool 
        return !isEmpty // put approprate code here
    



extension Sequence where Iterator.Element == Bool 

    func allTrue() -> Bool 
        return !contains(false)
    

【讨论】:

也许这个问题的解决方案确实是一种不同的方法。感谢您的回答。 是的,如果您正在编写响应式代码以使顺序以这种方式很重要,那么您做错了。【参考方案2】:

您可以通过一次订阅来处理您的数据验证。以下是我将组合事件绑定到执行组合结果验证的 Observable 的方法。

let fields = [firstNameObservable, 
              lastNameObservable, 
              bioObservable, 
              websiteObservable]
let validFields = Observable.combineLatest(fields).bind(to: validateFields)

将提供组合结果验证的 Observable 可能类似于以下函数。

func validateFields<T>(allFields: Observable<[T]>) -> Observable<Bool>

    return allFields.map  fieldData in
        var result: Bool = false

        /* Is the data valid? */

        return result;
    

每当有来自任何字段 Observables 的新数据时,Observable validFields 都会更新验证结果。使用combineLatest,您可以获得额外的好处,即您的源 Observables 的顺序得以保持。这个解决方案是在不借助状态存储使其完全反应的情况下完成的。

【讨论】:

我的目标实际上是确保在设置AccountDetails 属性之后执行验证。虽然您的回答很有趣,但它并不一定能为我的具体问题提供解决方案。

以上是关于RxSwift 共享订阅执行顺序的主要内容,如果未能解决你的问题,请参考以下文章

RxSwift之深入解析特殊序列deallocating与deallocated的源码实现

RxSwift 结合 observable 和条件

redis事务以及发布订阅

详谈:Redis事务和消息订阅

RxSwift:如何使用 shareReplay 懒惰地获取订阅

RxSwift - Observable.generate - 使用附加映射处理顺序请求