防抖、节流与限流控制
在处理用户输入或高频事件时,控制事件频率是性能优化的必修课。
防抖 (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 }