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

如何避免收到响应前重复调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:07:50