ReactiveCocoa延迟操作:throttle还是debounce?

有时当我们观察一个在不断变化的值的时候,除了需要相应的刷新UI之外,可能还要做点其他操作,比如网络请求之类的。典型的需求比如,实时搜索和购物车商品数量增减等。

我们这里以购物车商品数量增减为例简单谈下标题中的内容。


购物车中的商品点击加减号增减数量.png

很好理解,为了记录当前商品加购数量,点击加减号的同时我们需要将最新的值同步给服务器。但如果每点一次就发一次请求的话,显然不够合理,因为短时间的连续快速点击会造成大量的请求被浪费,我们只需要同步最后一次点击时的数据就可以了。所以我们需要做延迟请求,点一下之后等一会(比如1秒)再请求,如果在这段等待的时间间隔内又点击了,则再等一会。

用简单的系统API可以这样实现:

// 方法1:
private func delayUpdateNumber() {
    // 先取消上次的延迟操作
    NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(updateNumber), object: nil)
    // 延迟1秒再同步给服务器
    self.perform(#selector(updateNumber), with: nil, afterDelay: 1)
}
    
@objc private func updateNumber() { // update new number to server }

当然你可能可以用更高级的其他系统原生方法实现同样的目的。这里先不谈,我们继续往下看。

如果你的项目中使用了ReactiveSwift(ReactiveCocoa),那么你可以定义一个信号(比如叫:numberChangedSignal)来关联当前商品加购数量的变化,然后observe这个信号来向服务器同步数据,这时候我们可以用Signal.debounce或者Signal.throttle来达到“延迟”的目的:

private func delayUpdateNumber() {
    // 不做延迟
    numberChangedSignal.observeValues { [weak self] in
        self?.update($0)
    }
    // 方法2:用debounce“延迟”1秒
    numberChangedSignal.debounce(1, on: QueueScheduler.main).observeValues { [weak self] in
        self?.update($0)
    }
    // 方法3:用throttle“延迟”1秒
    numberChangedSignal.throttle(1, on: QueueScheduler.main).observeValues { [weak self] in
        self?.update($0)
    }
}

private func update(_ number: Int) { // update new number to server }

Signal.debounce可以达到和方法1同样的效果。在这里"debounce"是“去抖动”的意思,也就是当调用动作1秒后,才会执行该动作,若在这1秒内又调用此动作则将重新计算执行时间。
Signal.throttle则可以达到和方法1“类似”的效果,但不完全相同(实际上有很大的不同)。单词“throttle”是“节流、节流阀”的意思。根据文档,Signal.throttle的作用是:等信号中的上一个值发送之后,“节流”给定的时间间隔,再发送最新的值。

Forward the latest value on scheduler after at least interval seconds have passed since the returned signal last sent a value.

所以说它的作用是“延迟”有点不够准确。如本文中的例子,用Signal.throttle的效果是:点击第一下,发送第一个值,然后节流1秒(在这1秒之内不管值怎么变化都不会放到信号中),1秒到期后向信号流中加入当前时刻的最新值,再节流1秒,到期后再向信号流中加入最新的值,如此反复。

从源码来看,Signal的throttle和debounce二者都是借助DateScheduler来规划未来时间点Signal内的Events,感兴趣可以点进去看一看。有时间另写一篇二者源码实现上的对比。

所以,假如我们一直点加购商品的“+”,而任何两次相邻点击之间的间隔都小于1秒,那么直到你停止点击前方法1和方法2将永远也不会向服务器同步数据,而方法3则始终间隔1秒同步一次。所以哪种方法更合理呢?

你可能感兴趣的:(ReactiveCocoa延迟操作:throttle还是debounce?)