ArkTS 搜索旧请求覆盖新结果怎么办中式美食输入框怎么防止慢请求回写中式美食的搜索框不是等用户输入完才有动作。用户可能连续输入“番”“番茄”“番茄炒蛋”也可能一边删一边改。如果每次输入都发一次查询旧请求不一定先回来。最常见的现象是输入框已经是“番茄炒蛋”列表却显示“番”的结果。这个问题不要只靠防抖。防抖能减少请求次数但不能保证慢请求不会最后回来。我的做法是加一个requestVersion每次发请求都带版本号只有最新版本能回写页面。本文导读问题处理方式快速输入后结果乱跳每次搜索递增 requestVersion慢请求覆盖新结果回写前比较版本号清空输入框仍显示旧结果清空时也递增版本并回到默认列表搜索失败后页面乱保留关键词单独显示错误态环境和文件边界项目内容开发工具DevEco StudioHarmonyOS SDKAPI 12Stage 模型搜索页entry/src/main/ets/features/search/SearchPage.ets状态层entry/src/main/ets/features/search/SearchViewModel.ets数据层entry/src/main/ets/data/repository/RecipeSearchRepository.ets本文重点ArkTS 搜索请求时序、旧请求拦截、RDB 查询错误写法谁回来晚谁覆盖asynconKeywordChange(keyword:string):Promisevoid{this.keywordkeywordthis.loadingtruethis.resultawaitthis.repository.search(keyword)this.loadingfalse}这段代码最大的问题是没有判断“这个结果是不是当前输入框想要的结果”。请求 A 搜“番”请求 B 搜“番茄炒蛋”。如果 B 先回来页面显示正确A 后回来又把列表覆盖成旧结果。用 requestVersion 拦住旧回写exportclassSearchViewModel{privaterequestVersion:number0keyword:stringloading:booleanfalseerrorText:stringresult:RecipeCard[][]asyncsearch(keyword:string):Promisevoid{constversionthis.requestVersionthis.keywordkeywordthis.loadingtruethis.errorTexttry{constrowsawaitthis.repository.searchRecipe(keyword)if(version!this.requestVersion)returnthis.resultrows}finally{if(versionthis.requestVersion){this.loadingfalse}}}}这个版本号不是为了炫技就是一个很朴素的判断你回来时如果已经不是最新请求就不要改页面。清空输入框也要当成一次新请求asyncclearKeyword():Promisevoid{constversionthis.requestVersionthis.keywordthis.errorTextthis.loadingfalsethis.resultawaitthis.repository.queryDefaultRecommend()if(version!this.requestVersion){return}}清空输入框容易被忽略。用户点清空后旧搜索请求如果还在路上回来以后会把默认推荐覆盖掉。所以清空也要递增版本。Repository 只负责查不负责决定能不能回写exportclassRecipeSearchRepository{asyncsearchRecipe(keyword:string):PromiseRecipeCard[]{constkeykeyword.trim()constpredicatesnewrelationalStore.RdbPredicates(recipe)predicates.like(search_text,%${key}%)predicates.orderByDesc(score)predicates.orderByDesc(updated_at)predicates.limitAs(30)constrowsawaitthis.queryRows(predicates)returnrows.map(rowthis.toRecipeCard(row))}}Repository 不需要知道输入框现在是什么。它只负责把某个关键词查出来。能不能回写是 ViewModel 的职责。页面层只绑定状态Componentexportstruct SearchPage{StateviewModel:SearchViewModelnewSearchViewModel()build(){Column(){SearchInput({value:this.viewModel.keyword,onChange:(value:string)this.viewModel.search(value),onClear:()this.viewModel.clearKeyword()})SearchResultList({loading:this.viewModel.loading,errorText:this.viewModel.errorText,recipes:this.viewModel.result})}}}页面层不要再自己判断旧请求。它只要把输入动作交给 ViewModel然后根据 ViewModel 状态渲染。防抖可以加但不是唯一防线exportclassSearchDebouncer{privatetimer:number0run(task:()void,delay:number250):void{if(this.timer0){clearTimeout(this.timer)}this.timersetTimeout(task,delay)}}防抖的作用是少发请求requestVersion的作用是防止旧请求回写。两个解决的是不同问题不要把它们混成一个。回归验收记录步骤操作预期1快速输入“番”“番茄”“番茄炒蛋”最终只显示最后一个关键词结果2人为延迟第一个请求旧请求返回后不能覆盖新列表3清空输入框回到默认推荐不保留旧搜索结果4搜索失败保留关键词和错误态不乱写空结果5切换分类再搜索requestVersion 重新生效6返回搜索页输入框和结果保持一致排查顺序顺序先看什么为什么先看它1请求返回前有没有版本判断没有判断就会被旧请求覆盖2清空输入是否递增版本清空也可能被旧请求回写3loading 是否按版本关闭旧请求不能把新请求的 loading 关掉4防抖和版本号是否同时存在防抖减少请求版本号保证正确边界责任层级应该负责什么不应该负责什么SearchPage绑定输入框和结果列表判断哪个请求有效SearchViewModel维护 requestVersion 和页面状态拼复杂 SQLRepository根据关键词查 RDB决定是否回写页面Debouncer减少输入触发频率替代请求版本判断小结搜索页最怕的是“输入框和结果不一致”。用户看到这种错乱会直接怀疑搜索功能不可信。中式美食这里我会把时序控制放在 ViewModel输入变化递增版本请求返回前比较版本清空输入也递增版本。Repository 只查数据页面只渲染状态。这样即使网络慢、用户输入快旧请求也不能把新结果覆盖掉。