渐进式编程课
KMP 目录
第 38 课2026.08.24

用户输入搜索词时,每敲一个字符都会产生新值。如果立即查询,容易触发大量无效请求;debounce() 可以等用户短暂停顿后再继续处理。

第 38 课:debounce 处理搜索输入

知识点

使用 Flow 的 debounce() 等待输入稳定后,再向下游发送最新值。

用途

用户输入搜索词时,每敲一个字符都会产生新值。如果立即查询,容易触发大量无效请求;debounce() 可以等用户短暂停顿后再继续处理。

核心概念

  • debounce(500) 表示新值到来后等待 500 毫秒。
  • 等待期间若又有新值,旧值会被忽略,并从新值重新计时。
  • 输入停止后,最新值会被发送给下游。
  • debounce 目前需要使用 @OptIn(FlowPreview::class)。
  • 它只控制值的发送频率,不负责真正执行搜索。

示例代码

下面的 ViewModel 接收界面输入,并暴露已经稳定 500 毫秒的搜索词:

kotlin
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.stateIn

@OptIn(FlowPreview::class)
class SearchViewModel : ViewModel() {
    private val query = MutableStateFlow("")

    val stableQuery: StateFlow<String> = query
        .debounce(500)
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5_000),
            initialValue = "",
        )

    fun onQueryChange(value: String) {
        query.value = value
    }
}

例如用户在 500 毫秒内依次输入 K、KM、KMP,下游不会收到前两个中间值;停止输入 500 毫秒后,stableQuery 更新为 KMP。

5 分钟练习

把等待时间改为 800 毫秒,并补全下面的代码:

kotlin
val stableQuery: StateFlow<String> = query
    .________(________)
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = "",
    )

想一想:用户在 800 毫秒内连续输入 Ko、Kot、Kotlin,然后停止输入,最终会发送哪个值?

参考答案

kotlin
val stableQuery: StateFlow<String> = query
    .debounce(800)
    .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = "",
    )

最终发送 Kotlin,因为它是等待时间内出现的最新值。

与上一课的联系

上一课的 retryWhen() 在请求失败后控制是否再次尝试;今天的 debounce() 在请求开始前减少无效触发。两者分别从失败恢复和输入频率两个位置,让数据流更稳定。

参考资料