RxSwift - 如何限制缓冲区的时间跨度 [英] RxSwift - How to throttle buffer's time span

查看:43
本文介绍了RxSwift - 如何限制缓冲区的时间跨度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试重新创建一个代码片段,该代码片段主要计算按钮连续点击的次数.代码在 RxJS 中,我正尝试将其转换为 RxSwift 以用于学习目的,但无法弄清楚缓冲区和节流部分.

I am trying to recreate a code snippet that basically counts how many times a button was clicked in a row. The code is in RxJS and I am trying to convert it to RxSwift for learning purposes but can't figure out the buffer and throttle part.

可以在jsfiddle上查看js代码

目前我有这个

  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 示例中一样.

And I can't figure out how can I delay until tapping ends and collect all values since the last emission like in the RxJS example.

推荐答案

您遇到的问题是因为 RxSwift buffer 操作符不像 RxJS buffer 那样工作操作员.它更像 RxJS bufferWithTimeOrCount 操作符.

The problem you are having is because the RxSwift buffer operator doesn't work like the RxJS buffer operator. It works more like the RxJS bufferWithTimeOrCount operator.

目前,从 3.4.0 版本开始,没有与 buffer 等效的运算符.它的签名类似于 func buffer(_ boundary: Observer) ->可观察的<[E]>

Currently, as of version 3.4.0, there is no equivalent to the buffer operator. It's signature would be something like func buffer(_ boundary: Observer<BoundaryType>) -> Observable<[E]>

回答这个问题很有趣.我最终制作了一个缓冲区运算符,我在这个答案的底部提供了它.下面是我将如何写出在 Andre 的代码中定义的解决方案:

This has been a fun question to answer. I ended up making a buffer operator which I provide at the bottom of this answer. Here is how I would write the solution out that is defined in Andre's code:

    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.我不完全确定他为什么制作了两个不同的流...

The above code should be placed in the view controller's viewDidLoad method. There is one big change and one small change that I made. The small change is that I used debounce instead of throttle. Again, I think RxJS's throttle works differently than RxSwift's throttle does. The big change is that I combined his multiClickStream and singleClickStream. I'm not entirely sure why he made two separate streams...

我所做的另一项更改是将所有影响标签的 observable 滚动到一个标签可以绑定的 observable 中,而不是具有不同的 observable.我觉得这样更干净.

Another change I made was to roll all the observables that affect the label into one observable that the label could bind to, instead of having different ones. I think this is cleaner.

下面是我定义的缓冲区操作符.

Below is the buffer operator that I defined.

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])
        }
    }
}

这篇关于RxSwift - 如何限制缓冲区的时间跨度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆