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

Alamofire+PromiseKit并行请求超时异常:180秒设置未生效

问题描述

使用Alamofire + PromiseKit批量获取物品列表,部分请求耗时较长。已将Alamofire的SessionManager超时时间设为180秒,但首次调用getRemoteItem后60秒,所有未完成请求均进入print("IN FAIL 2")分支,报错如下:

responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.jsonSerializationFailed(error: Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around line 1, column 0." UserInfo={NSDebugDescription=Invalid value around line 1, column 0., NSJSONSerializationErrorIndex=0}))

getItems方法会并行发起所有物品请求,推测问题和并行有关,但不清楚180秒超时未生效的原因,相关代码如下:

func getItems(ids:[Int]) -> [Promise<Bool>] {
    let totalCount = ids.count
    
    return ids.map { index -> Promise<Bool> in
        return firstly {
            getRemoteItem(id: index, totalCount: totalCount)
        }
    }
}


func getRemoteItem(id:Int, totalCount:Int) -> Promise<Bool> {
    let headers = ["Authorization": "JWT \(userToken)"]
    return Promise { seal in

        let url = baseURL + "/api/items"
        let itemParams: Parameters = ["item_id": id]
        
        alamoFireManager!.request(URL(string: url)!, method: .post, parameters: itemParams, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
            switch response.result {
            case .success:
                if let jsonObject = response.result.value {
                    firstly {
                        self.saveItemToLocalStorage(jsonObject: jsonObject as! [String: Any], responseData: response.data!, totalCount: totalCount)
                    }.done { status in
                        seal.fulfill(status)
                    }.catch { error in
                        seal.reject(error)
                    }
                } else if let error = response.error {
                    print("IN FAIL 1")
                    seal.reject(error)
                }
            case .failure(let error):
                print("IN FAIL 2")
                seal.reject(error)
            }
        }
    }
}


private lazy var alamoFireManager: SessionManager? = {
    let configuration = URLSessionConfiguration.default
    configuration.timeoutIntervalForRequest = 180
    configuration.timeoutIntervalForResource = 180
    let alamoFireManager = Alamofire.SessionManager(configuration: configuration)
    return alamoFireManager
}()
问题分析与解决方案

核心问题定位

  1. 超时未生效:自定义的alamoFireManager是可选类型且懒加载,若初始化时机晚于请求发起,会默认使用Alamofire自带的SessionManager(默认超时60秒),导致自定义配置失效。
  2. JSON序列化失败:60秒超时后服务器可能返回非JSON格式的错误内容(如HTML错误页),或并行请求过载导致服务器返回空数据,触发序列化失败报错。

具体修复步骤

  • 确保自定义SessionManager生效:
    去掉可选类型,强制初始化,避免使用默认实例:

    private lazy var alamoFireManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 180
        configuration.timeoutIntervalForResource = 180
        return SessionManager(configuration: configuration)
    }()
    

    调用时直接使用alamoFireManager.request(...),无需强制解包!,规避nil风险。

  • 限制并行请求数量:
    默认httpMaximumConnectionsPerHost为6,过多并行请求会导致队列阻塞或服务器限流。根据服务器承载能力调整并发数:

    configuration.httpMaximumConnectionsPerHost = 10 // 可根据实际情况修改
    
  • 添加失败日志排查:
    在IN FAIL 2分支打印原始响应内容,确认服务器返回的实际数据:

    case .failure(let error):
        print("IN FAIL 2 - Error: \(error)")
        if let data = response.data, let responseStr = String(data: data, encoding: .utf8) {
            print("Server Raw Response: \(responseStr)")
        }
        seal.reject(error)
    
  • 先获取原始数据再解析JSON:
    替换responseJSON为responseData,手动解析JSON,避免序列化失败直接进入failure分支:

    alamoFireManager.request(...).responseData { response in
        switch response.result {
        case .success(let data):
            do {
                let jsonObject = try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
                firstly {
                    self.saveItemToLocalStorage(jsonObject: jsonObject, responseData: data, totalCount: totalCount)
                }.done { status in
                    seal.fulfill(status)
                }.catch { error in
                    seal.reject(error)
                }
            } catch {
                print("JSON解析失败: \(error)")
                seal.reject(error)
            }
        case .failure(let error):
            print("IN FAIL 2 - 请求失败: \(error)")
            seal.reject(error)
        }
    }
    
  • 优化PromiseKit批量请求管理:
    使用when(fulfilled:)统一管理批量Promise,便于统一处理所有请求的完成与失败:

    func getItems(ids:[Int]) -> Promise<[Bool]> {
        let promises = ids.map { getRemoteItem(id: $0, totalCount: ids.count) }
        return when(fulfilled: promises)
    }
    

内容的提问来源于stack exchange,提问作者KvnH

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:30:40