如何避免收到响应前重复调用API?API重复调用报错排查
看起来你遇到了iOS网络请求里很常见的重复调用导致的**-999错误(请求被取消)**问题——当你在未收到前一个API响应时就重复调用请求方法,系统会自动取消旧的请求,就出现了你控制台里的日志。下面给你几个实用的解决方案,根据你的业务场景选择即可:
解决方案:防止重复API调用的几种实现方式
1. 追踪请求状态,跳过重复调用
最直接的方式是维护一个布尔变量,标记当前是否有同类型请求在进行。发起新请求前先检查这个状态,只有当没有请求在执行时才继续。
示例代码(Swift):
// 用类变量追踪请求状态,如需线程安全可加锁或使用原子属性 private var isFetchingNews = false func fetchScienceNews(page: Int) { // 已有请求在处理时直接返回 guard !isFetchingNews else { print("已有新闻请求在处理,跳过本次调用") return } isFetchingNews = true let urlString = "https://newsapi.org/v2/top-headlines?apiKey=YOUR_API_KEY&pageSize=20&category=science&page=\(page)" guard let url = URL(string: urlString) else { isFetchingNews = false return } URLSession.shared.dataTask(with: url) { [weak self] data, response, error in // 无论请求成功/失败,完成后都重置状态 defer { self?.isFetchingNews = false } if let error = error { // 主动取消的-999错误可直接忽略 if (error as NSError).code == -999 { return } print("请求失败:\(error.localizedDescription)") return } // 处理返回数据,更新UI记得切回主线程 DispatchQueue.main.async { // 更新UI逻辑... } }.resume() }
2. 取消旧请求,只保留最新请求
如果你的场景是需要最新的请求结果(比如用户快速切换分页),可以在发起新请求前取消之前未完成的请求,确保只处理最新的响应。
示例代码:
// 保存当前请求的引用 private var currentNewsTask: URLSessionDataTask? func fetchScienceNews(page: Int) { // 取消之前未完成的请求 currentNewsTask?.cancel() let urlString = "https://newsapi.org/v2/top-headlines?apiKey=YOUR_API_KEY&pageSize=20&category=science&page=\(page)" guard let url = URL(string: urlString) else { return } // 创建新请求并保存引用 currentNewsTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in // 主动取消的-999错误直接忽略 if let error = error, (error as NSError).code == -999 { return } if let error = error { print("请求失败:\(error.localizedDescription)") return } // 处理返回数据... DispatchQueue.main.async { // 更新UI逻辑... } // 请求完成后清空引用 self?.currentNewsTask = nil } currentNewsTask?.resume() }
3. 用OperationQueue管理请求队列
如果你的请求场景更复杂(比如有多个不同类型的请求需要控制并发),可以使用OperationQueue,设置最大并发数为1,确保同一时间只有一个请求执行。
示例代码:
private let newsRequestQueue = OperationQueue() init() { // 设置队列最大并发数为1,保证请求串行执行 newsRequestQueue.maxConcurrentOperationCount = 1 newsRequestQueue.qualityOfService = .userInitiated } func fetchScienceNews(page: Int) { let fetchOperation = BlockOperation { let urlString = "https://newsapi.org/v2/top-headlines?apiKey=YOUR_API_KEY&pageSize=20&category=science&page=\(page)" guard let url = URL(string: urlString) else { return } // 同步请求运行在后台队列,不会阻塞主线程 do { let data = try Data(contentsOf: url) // 解析数据后切回主线程更新UI DispatchQueue.main.async { // 更新UI逻辑... } } catch { print("请求失败:\(error.localizedDescription)") } } newsRequestQueue.addOperation(fetchOperation) }
额外注意事项
- 在页面销毁时(比如
deinit方法),记得取消未完成的请求,避免内存泄漏:
deinit { currentNewsTask?.cancel() newsRequestQueue.cancelAllOperations() }
- 如果你使用Alamofire等第三方网络库,它们通常内置了请求管理功能,比如Alamofire的
Session可以自动处理重复请求的取消,你可以直接利用这些特性简化代码。
内容的提问来源于stack exchange,提问作者O. Shulzhenko
相关产品推荐
相关产品推荐

