Swift环境下如何实现两个HTTP请求的同步有序执行?寻求标准优化方案
解决Swift中顺序执行HTTP请求的最优方案
嘿,我来帮你搞定这个问题~你现在用循环+标记变量的方式实现顺序请求,确实存在代码冗余、容易出bug的问题,而且效率不高。先逐个分析你提到的几个方案:
- 循环+标记变量:这种方式不仅代码啰嗦,还容易因为标记变量的状态管理(比如忘记重置)导致逻辑错误,而且空循环等待会浪费CPU资源,完全不推荐。
- DispatchSemaphore:如果在主线程调用
semaphore.wait(),确实会阻塞UI线程,导致界面卡顿;就算在后台线程使用,也会让异步代码变得更复杂,不符合Swift现代异步编程的趋势,没必要用。 - NSLock:它主要用于多线程环境下的资源互斥访问,不是用来处理异步任务顺序执行的工具,用在这里属于误用,会增加不必要的复杂度。
最优方案:正确利用Swift的Async/Await
你已经把函数声明为async了,但里面还是用了传统的dataTask回调,这等于没发挥Async/Await的优势。Swift的URLSession已经提供了异步版本的API(data(for:)),用它可以让你的代码逻辑更清晰,天然支持顺序执行,完全不需要标记变量或者同步锁。
第一步:改造请求函数为纯Async/Await风格
先把你的doHTTPRecentProjectsCall改成异步抛出错误的版本,去掉回调:
func doHTTPRecentProjectsCall(employeeId: String) async throws { let dateFormatter = DateFormatter() dateFormatter.dateFormat = "yyyyMMdd" let todaysDate = dateFormatter.string(from: Date()) // 用字符串插值简化URL拼接,更易读 guard let reqUrl = URL(string: "http://192.168.179.185:8160/api/v1/project/recent/\(employeeId)?date=\(todaysDate)") else { throw NSError(domain: "InvalidURL", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"]) } var req = URLRequest(url: reqUrl) req.httpMethod = "GET" req.setValue("CC0001", forHTTPHeaderField: "CC-Tenant") req.setValue("BE", forHTTPHeaderField: "CC-Product") let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" formatter.timeZone = TimeZone(abbreviation: "UTC") // 这里应该是UTC?你之前写的ETC可能是笔误 formatter.locale = Locale(identifier: "en_US_POSIX") // 避免地区格式差异 // 使用异步版本的data(for:),自动等待请求完成 let (data, response) = try await URLSession.shared.data(for: req) // 可选:处理HTTP状态码,比如非200系列抛出错误 if let httpResponse = response as? HTTPURLResponse, !(200...299).contains(httpResponse.statusCode) { throw NSError(domain: "HTTPError", code: httpResponse.statusCode, userInfo: [NSLocalizedDescriptionKey: "Request failed with status code \(httpResponse.statusCode)"]) } let decoder = JSONDecoder() decoder.dateDecodingStrategy = .formatted(formatter) recentProjects = try decoder.decode(Array<Project>.self, from: data) // 清空旧数据,避免残留 recentProjectsDic.removeAll() recent.removeAll() for val in recentProjects { recentProjectsDic[val.name] = val recent.append(val.name) } print("test1") }
用同样的方式改造你的第二个请求doHTTPProjectsCall,让它也变成async throws函数。
第二步:顺序调用请求
现在你可以直接按顺序调用两个函数,await会自动等待前一个请求完成后再执行下一个,完全不需要标记变量:
// 用Task包裹异步逻辑,避免在同步上下文直接调用await Task { while !Task.isCancelled && projects.isEmpty { do { // 先执行第一个请求,等待完成 try await doHTTPRecentProjectsCall(employeeId: self.employeeId) // 第一个请求完成后,自动执行第二个 try await doHTTPProjectsCall(token: apiKey, employeeId: self.employeeId) } catch { // 统一处理请求错误 print("Request failed: \(error.localizedDescription)") } if projects.isEmpty { do{ try await Task.sleep(nanoseconds: 2_000_000_000) } catch { print("Sleep interrupted: \(error)") } } } }
为什么这是最优解?
- 代码简洁清晰:去掉了回调嵌套和标记变量,逻辑一目了然,可读性极强。
- 天然支持顺序执行:
await关键字会暂停当前异步任务,等待前一个请求完成后再继续,完全符合你的需求。 - 规范的错误处理:用
throws和do/catch统一处理所有错误(URL无效、请求失败、解析错误等),避免遗漏异常情况。 - 非阻塞主线程:Async/Await基于Swift的结构化并发模型,会自动管理线程,不会阻塞主线程,保证界面流畅。
内容的提问来源于stack exchange,提问作者Pjaks
相关产品推荐
相关产品推荐

