RxSwift - 如何限制缓冲区的时间跨度

时间:2017-04-07 19:38:50

标签: swift rxjs rx-swift reactivex

我正在尝试重新创建一个代码段,该代码段基本上计算连续单击按钮的次数。代码在RxJS中,我试图将它转换为RxSwift用于学习目的但是可以找出缓冲区和节流部分。

You can see the js code on jsfiddle

目前我有这个

  tapButton.rx.tap      
  .buffer(timeSpan: 0.25, count: 10, scheduler: MainScheduler.instance)
  .map {$0.count}
  .filter { $0 >= 2 }
  .subscribe(onNext: { events in
    print(events)
  }).addDisposableTo(disposeBag)

我无法弄清楚如何点击结束并收集自上一次发射以来的所有值,如同在RxJS示例中一样,我怎么才能延迟。

1 个答案:

答案 0 :(得分:2)

您遇到的问题是因为RxSwift buffer运算符不像RxJS buffer运算符那样工作。它更像是RxJS bufferWithTimeOrCount运算符。

目前,从版本3.4.0开始,没有与buffer运算符等效的内容。它的签名就像func buffer(_ boundary: Observer<BoundaryType>) -> Observable<[E]>

这是一个很有趣的问题。我最后制作了一个缓冲区操作符,我在这个答案的底部提供了这个操作符。以下是我将如何编写安德烈代码中定义的解决方案:

    let trigger = button.rx.tap.debounce(0.25, scheduler: MainScheduler.instance)
    let clickStream = button.rx.tap.asObservable()
        .buffer(trigger)
        .map { $0.count }
        .map { $0 == 1 ? "click" : "\($0)x clicks" }

    let clearStream = clickStream
        .debounce(10.0, scheduler: MainScheduler.instance)
        .map { _ in "" }

    Observable.merge([clickStream, clearStream])
        .bind(to: label.rx.text)
        .disposed(by: bag)

上述代码应放在视图控制器的viewDidLoad方法中。我做了一个很大的改变和一个小的改变。微小的变化是我用debounce而不是油门。同样,我认为RxJS的油门工作方式与RxSwift的油门工作方式不同。最大的变化是我将他的multiClickStream和singleClickStream结合起来。我不完全确定他为什么要分两个流......

我做的另一个改变是将影响标签的所有可观察对象滚动到一个标签可以绑定的可观察对象,而不是具有不同的可观察对象。我认为这更清洁了。

下面是我定义的缓冲区运算符。

extension Observable {

    /// collects elements from the source sequence until the boundary sequence fires. Then it emits the elements as an array and begins collecting again.
    func buffer<U>(_ boundary: Observable<U>) -> Observable<[E]> {
        return Observable<[E]>.create { observer in
            var buffer: [E] = []
            let lock = NSRecursiveLock()
            let boundaryDisposable = boundary.subscribe { event in
                lock.lock(); defer { lock.unlock() }
                switch event {
                case .next:
                    observer.onNext(buffer)
                    buffer = []
                default:
                    break
                }
            }
            let disposable = self.subscribe { event in
                lock.lock(); defer { lock.unlock() }
                switch event {
                case .next(let element):
                    buffer.append(element)
                case .completed:
                    observer.onNext(buffer)
                    observer.onCompleted()
                case .error(let error):
                    observer.onError(error)
                    buffer = []
                }
            }
            return Disposables.create([disposable, boundaryDisposable])
        }
    }
}