【问题标题】:Handle next events after seeing a specific number of them在看到特定数量的事件后处理下一个事件
【发布时间】:2017-06-05 15:23:18
【问题描述】:

我正在尝试做的是订阅一个发出Enums 序列的可观察对象。目标是每次我看到 3 个特定类型的 Enums 时都会调用我的 onNext。以下是我尝试过的但是,它只能工作一次。它没有继续下去。我想知道处理这个问题的最佳方法是什么。

enum Baseball {
 case strike, ball, hit
}

let bag = DisposeBag()
let subject = PublishSubject<Baseball>()

subject.filter { $0 == .strike }
  .elementAt(2)
  .subscribe(onNext: { _ in print("3 Strikes you're out") 
}).addDisposableTo(bag)

// First batter
subject.onNext(.strike)
subject.onNext(.ball)
subject.onNext(.ball)
subject.onNext(.ball)
subject.onNext(.strike)
subject.onNext(.strike) // 3 Strikes you're out is printed

// Second batter
subject.onNext(.ball)
subject.onNext(.ball)
subject.onNext(.hit)

// Third batter
subject.onNext(.strike)
subject.onNext(.strike)
subject.onNext(.strike) // Would like this to fire as well

【问题讨论】:

    标签: swift rx-swift


    【解决方案1】:

    使用缓冲区操作符:

    subject.filter { $0 == .strike }
        .buffer(timeSpan: 3e7, count: 3, scheduler: MainScheduler.instance)
        .subscribe(onNext: { print("3 Strikes you're out") })
        .addDisposableTo(bag)
    

    现在,每当有 3 次罢工或大约每年一次时,它就会发出。

    如果您不喜欢它每年都会超时的事实,您可以编写自己的缓冲区运算符来计算:

    extension Observable {
        func buffer(count: Int) -> Observable<[E]> {
            return Observable<[E]>.create { observer in
                var elements: [E] = []
                let lock = NSRecursiveLock()
                return self.subscribe { event in
                    switch event {
                    case .completed:
                        observer.onCompleted()
                    case .error(let error):
                        observer.onError(error)
                    case .next(let element):
                        lock.lock(); defer { lock.unlock() }
                        elements.append(element)
                        if elements.count == count {
                            observer.onNext(elements)
                            elements = []
                        }
                    }
                }
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      这是一个有趣的练习。

      缓冲区

      如@daniel-t 所示,这种方法需要容量为 3 的.buffer,当它已满时,您知道有 3 次罢工。

      计数器

      除了buffer的方法,你还可以使用计数器:

      let subject = PublishSubject<Baseball>()
      
      let strikes = subject.asObservable()
          .filter { $0 == .strike }
      let ticker = strikes.map { _ in () }
      let counter = ticker.scan(0) { (memo, _) -> Int in memo + 1 }
      
      strikes.withLatestFrom(counter) { strike, count in (strike, count) }
          .filter { _, count in count % 3 == 0 }
          .subscribe(onNext: { _ in print("3 Strikes you're out")
          }).addDisposableTo(bag)
      

      与一两个操作员一起,也可以使递增的罢工计数器在 3 后重置为 0。

      索引作为计数器

      let subject = PublishSubject<Baseball>()
      
      let strikes = subject.asObservable()
          .filter { $0 == .strike }
      // Unlike the counter, this starts at 0 and reports "% 3 == 0" too early.
      strikes.mapWithIndex { _, index in index + 1 }
          .filter { $0 % 3 == 0 }
          .subscribe(onNext: { _ in print("3 Strikes you're out")
          }).addDisposableTo(bag)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-11-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-09
        • 1970-01-01
        • 2016-03-22
        • 2022-11-02
        相关资源
        最近更新 更多