Alamofire使用串行队列配置后请求乱序,如何维持请求与结果FIFO顺序?
问题根因
你配置的串行队列仅作用于Alamofire内部的任务调度逻辑,不会限制网络请求本身的执行与返回顺序:
- 你开启了
startRequestsImmediately = true,请求创建完成后会直接提交到底层URLSession执行,而URLSession默认会并发处理所有请求,不同请求的网络响应时间受接口响应速度、网络波动影响天然不固定,自然会出现后创建的请求先返回的情况 - 串行序列化队列仅保证序列化任务按入队顺序执行,但如果网络响应返回顺序本身是乱序的,序列化任务的入队顺序自然也是乱的,最终输出顺序就不符合预期
实现请求与结果FIFO的两种方案
方案1:请求串行执行(上一个请求完成后再发起下一个)
适合请求之间存在强依赖、必须按顺序执行的场景,实现逻辑简单:
- 首先修改Session配置,关闭自动启动请求:
let queue = DispatchQueue(label: "APIManager.rootQueue") a.session = Session(configuration: sessionConfiguration, delegate: a, rootQueue: queue, startRequestsImmediately: false, // 关闭自动启动 requestQueue: queue, serializationQueue: queue, interceptor: nil, serverTrustManager: nil, redirectHandler: nil, cachedResponseHandler: nil, eventMonitors: [])
- 维护请求等待队列,按顺序触发请求执行:
// 全局维护请求队列与执行状态 private var pendingRequests: [Request] = [] private var isRunningRequest = false // 对外暴露的请求入队方法 func addRequestToQueue(_ request: Request) { pendingRequests.append(request) runNextRequestIfPossible() } private func runNextRequestIfPossible() { guard !isRunningRequest, !pendingRequests.isEmpty else { return } isRunningRequest = true let currentRequest = pendingRequests.removeFirst() // 监听请求完成回调 currentRequest.response(queue: queue) { [weak self] response in guard let self = self else { return } // 此处处理你的响应、序列化逻辑 // 执行完成后触发下一个请求 self.isRunningRequest = false self.runNextRequestIfPossible() } currentRequest.resume() }
方案2:请求并发执行,结果按创建顺序回调
适合需要并发请求提升效率、但要求回调顺序和创建顺序一致的场景,性能更高:
// 全局维护结果缓存、当前待回调的序号、请求创建序号 private var resultCache: [Int: Any] = [:] private var nextCallbackIndex = 0 private var requestSequence = 0 // 对外暴露的请求方法 func sendRequestWithOrderedCallback(_ request: Request, callback: @escaping (Any) -> Void) { let currentIndex = requestSequence requestSequence += 1 // 监听请求响应 request.response(queue: queue) { [weak self] response in guard let self = self else { return } // 先把结果存入缓存 self.resultCache[currentIndex] = response // 按顺序触发所有已返回的请求回调 while let readyResult = self.resultCache[self.nextCallbackIndex] { callback(readyResult) self.resultCache.removeValue(forKey: self.nextCallbackIndex) self.nextCallbackIndex += 1 } } request.resume() }
注意事项
- 方案1会牺牲请求并发性能,仅适合请求间有强依赖的场景
- 方案2需要注意内存占用,如果出现大量请求长时间未返回的情况,缓存的结果会持续占用内存,可根据业务需求添加超时清理逻辑
内容的提问来源于stack exchange,提问作者Peter Lapisu
相关产品推荐
相关产品推荐

