Skip to content

防抖、节流与限流控制

在处理用户输入或高频事件时,控制事件频率是性能优化的必修课。

防抖 (Debounce)

核心逻辑:“你尽管动,停下来算我输”。只有当上游连续 timeout 毫秒内没有发射新值时,才发射最新的那个值。

  • 场景:搜索框联想。用户打字 "A", "An", "And", "Andr"... 我们不想每打一个字就请求一次网络,只想等用户打完停顿后(如 300ms)再请求。
kotlin
searchFlow
    .debounce(300) // 停顿 300ms 后才发射
    .flatMapLatest { query -> searchApi(query) }
    .collect { ... }

采样 (Sample)

核心逻辑:“每隔 X 秒看一眼”。 不管上游多快,我每隔固定的时间周期取一个最新的值。如果没有新值,该周期就不发射。

  • 场景:UI 刷新(如 FPS 显示、传感器数据可视化),人眼只能看 60Hz,传感器发 1000Hz 也是浪费。
kotlin
sensorFlow
    .sample(16) // 约 60fps 的采样率
    .collect { updateUi(it) }

节流 (Throttle)

Flow 标准库没有原生提供 throttle 操作符,但我们可以轻松实现。

throttleFirst (防连击)

接收第一个值,然后忽略接下来窗口期内的所有值。

kotlin
fun <T> Flow<T>.throttleFirst(windowDuration: Long): Flow<T> = flow {
    var lastEmissionTime = 0L
    collect { value ->
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastEmissionTime > windowDuration) {
            lastEmissionTime = currentTime
            emit(value)
        }
    }
}

throttleLast

类似于 sample,但通常指固定窗口内的最后一个。

过滤重复 (Distinct)

  • distinctUntilChanged(): 只有当新值与上一个值不同时才发射。
  • distinctUntilChangedBy { ... }: 根据对象的某个字段判断是否重复。
kotlin
// 只有当 User 对象的 name 字段改变时才发射
userFlow.distinctUntilChangedBy { it.name }