You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修正带Debounce的API搜索请求?解决重复触发问题

问题原因分析

你遇到的重复API请求问题,核心原因是每次输入文本变更时都会调用getSearchedNotifications,而这个函数内部每次都会启动一个新的协程去订阅_searchText流。输入"acc"三次就会创建3个独立的collect协程,当debounce延迟结束后,每个订阅的流都会收到最新的"acc"值,最终触发3次重复请求。

解决方案

只需要对ViewModel和UI做两处关键修改,确保流的订阅仅执行一次,而非每次输入都重复订阅:

1. 修改ViewModel:初始化时一次性订阅流

将防抖和API请求逻辑移到ViewModel的init块中,确保只启动一次流订阅;同时用combine处理分页参数(如果需要动态调整),避免硬编码。

private val _searchText = MutableStateFlow("")
val searchText: StateFlow<String> = _searchText

// 用StateFlow管理分页参数,支持动态调整
private val _paginationParams = MutableStateFlow(Pair(5, 0))

@OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class)
init {
    viewModelScope.launch {
        // 合并搜索文本和分页参数,统一处理
        combine(_searchText, _paginationParams) { text, (take, skip) ->
            Triple(text, take, skip)
        }
            .debounce(2000L) // 2秒防抖
            .distinctUntilChanged() // 仅文本或参数变化时触发
            .collectLatest { (searched, take, skip) ->
                val response = if (searched.isEmpty()) {
                    notificationsRepository.getNotifications(take, skip, "")
                } else {
                    notificationsRepository.getNotifications(take, skip, searched)
                }

                when (response) {
                    is Resource.Success -> {
                        _notifications.update { response.data }
                    }
                    is Resource.Error -> {
                        // 这里添加错误处理逻辑
                    }
                    else -> {}
                }
            }
    }
}

// 仅负责更新搜索文本
fun updateSearchedText(newText: String) {
    _searchText.value = newText
}

// 如需调整分页参数,调用此方法
fun updatePaginationParams(take: Int, skip: Int) {
    _paginationParams.value = take to skip
}

2. 修改UI:仅更新搜索文本

移除onValueChange中对getSearchedNotifications和getAllNotifications的调用,只保留更新搜索文本的逻辑,流会自动处理后续的防抖和请求。

val searchedText by viewModel.searchText.collectAsState()

OutlinedTextField(
    modifier = Modifier
        .fillMaxWidth()
        .padding(15.dp),
    value = searchedText,
    onValueChange = { newText ->
        viewModel.updateSearchedText(newText)
    }
)
关键优化点说明
  • 单一订阅:流的订阅仅在ViewModel初始化时执行一次,避免多次订阅导致的重复请求。
  • collectLatest:如果用户在防抖延迟期间继续输入,未完成的API请求会被自动取消,仅保留最新的一次请求,完全符合防抖的预期行为。
  • 参数动态化:用combine合并搜索文本和分页参数,后续调整分页时只需更新_paginationParams,无需修改流的订阅逻辑。

内容的提问来源于stack exchange,提问作者Low Pre Intern

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 21:37:45