Swift中使用Async/Await实现搜索方法的线程问题
问题排查与解决方案
核心问题分析
当前实现存在两个关键逻辑缺陷,导致快速输入时旧搜索请求的结果覆盖最新请求的结果:
- 捕获过时的timestamp进行校验:
searchLocalPopularTerms中,guard let timestamp else { return }在Task外部执行,每个Task会捕获方法调用瞬间的本地timestamp值,而非异步请求返回时的最新本地timestamp。当旧请求(如输入"Sandwi"时发起的请求)比新请求(输入"Sandwiches"时的请求)晚返回时,它会用调用时的旧timestamp(如T0)和自身请求的timestamp(T1)比较,只要T1>T0就会更新结果,完全忽略此时本地已存在的、来自新请求的更新后timestamp(如T2)。 - 更新本地timestamp时使用非请求专属时间:更新本地timestamp时使用
Date()而非当前请求返回的searchResults.timestamp,这会导致timestamp校验逻辑失去准确性——新生成的Date()与请求发起时的timestamp存在时间偏差,无法严格对应请求的发起顺序。
修正后的代码实现
1. 修正searchLocalPopularTerms方法
@MainActor func searchLocalPopularTerms(for searchTerm: String, completionHandler: @escaping (() -> Void)) { // 为当前请求生成专属timestamp,确保按发起顺序严格递增 let requestTimestamp = Date() // 初始化本地timestamp(仅首次请求需要) if self.timestamp == nil { self.timestamp = requestTimestamp } Task { let searchResults = await self.searchManager.searchResults( searchTerm: searchTerm, gender: self.gender, timestamp: requestTimestamp ) // 关键:获取当前最新的本地timestamp进行校验,而非捕获的旧值 guard let currentLocalTimestamp = self.timestamp else { completionHandler() return } // 仅当当前请求的timestamp晚于最后一次成功更新的timestamp时,才更新结果 if searchResults.results.isNotEmpty, searchResults.timestamp > currentLocalTimestamp { self.searchItems = searchResults.results // 使用请求的timestamp更新本地存储,确保标记的是请求发起顺序 self.timestamp = searchResults.timestamp } completionHandler() } }
2. (可选)优化SearchManager的searchResults方法
为参数重命名,明确语义,避免歧义:
private static func searchResults( _ query: String, gender: Gender, popularTerms: [String]? = nil, allowFredhopperSuggestions: Bool = ConfigManager.shared.allowFredhopperSuggestions, requestTimestamp: Date // 重命名为requestTimestamp,明确是请求发起时的时间戳 ) async -> SearchResults { let suggestionLimits = SearchSuggestionLimits() var suggestions: [SearchItem] = [] suggestions.reserveCapacity(suggestionLimits.totalLimit) if allowFredhopperSuggestions { let results = await fredhopperSuggestions(query) suggestions.append(contentsOf: results) } else { let designers = designerSearchResults(query, gender: gender, fetchLimit: suggestionLimits.designerLimit) suggestions.append(contentsOf: designers) let categories = categorySearchResults(query, gender: gender, fetchLimit: suggestionLimits.categoryLimit) suggestions.append(contentsOf: categories) } if let popularTerms = popularTerms { let alreadyFoundList = suggestions.compactMap { $0.name } let popularSearches = popularTermsSearchResults(query, popularTerms: popularTerms, alreadyFoundList: alreadyFoundList, fetchLimit: suggestionLimits.popularLimit) suggestions.append(contentsOf: popularSearches) } return SearchResults(timestamp: requestTimestamp, results: suggestions) }
修正逻辑说明
- 请求专属timestamp:在方法调用时生成
requestTimestamp,确保每个请求的时间标记严格按发起顺序递增,不受Task执行顺序影响。 - 实时校验最新本地timestamp:异步请求返回后,重新获取
self.timestamp的当前值进行校验,确保只有最晚发起的请求(timestamp最大)才能更新结果。 - 用请求timestamp更新本地存储:避免使用
Date()带来的时间偏差,确保本地timestamp严格对应最后一次成功更新的请求发起时间。
内容的提问来源于stack exchange,提问作者DevB1
相关产品推荐
相关产品推荐

