Swift中连续调用多个依赖API的最优实现方案咨询
解决Swift回调地狱的优化方案
嘿,这个问题太有代表性了——这种层层嵌套的回调(也就是大家常说的“回调地狱”)确实会让代码越写越乱,尤其是当API依赖链变长,比如要加D、E、F的时候,维护起来简直头疼。针对Swift的场景,我给你整理了几个实用的优化方案,从最推荐的到兼容老项目的都有:
方案1:使用Swift async/await(首推,Swift 5.5+)
从Swift 5.5开始,官方引入了原生的异步等待语法,这是解决回调地狱最简洁优雅的方式。首先我们需要把原来的带completionHandler的API函数改造成异步函数:
// 改造API A为异步函数 func callAFunction() async throws -> [String: String] { return try await withCheckedThrowingContinuation { continuation in self.callAFunction(completionHandler: { isSuccess, result, errorMsg in if isSuccess { continuation.resume(returning: result) } else { continuation.resume(throwing: NSError(domain: "APIError", code: -1, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } } // 同理改造API B func callBFunction(aResponseString: String) async throws -> [String: String] { return try await withCheckedThrowingContinuation { continuation in self.callBFunction(aResponseSting: aResponseString, completionHandler: { isSuccess, result, errorMsg in if isSuccess { continuation.resume(returning: result) } else { continuation.resume(throwing: NSError(domain: "APIError", code: -2, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } } // 改造API C func callCFunction(bResponseString: String) async throws -> [String: String] { return try await withCheckedThrowingContinuation { continuation in self.callCFunction(bResponseSting: bResponseString, completionHandler: { isSuccess, result, errorMsg in if isSuccess { continuation.resume(returning: result) } else { continuation.resume(throwing: NSError(domain: "APIError", code: -3, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } }
然后调用的时候,代码就变成了线性执行的逻辑,完全没有嵌套:
func testAPI() { // 在异步上下文调用(比如Task里) Task { do { let dataA = try await callAFunction() guard let aResponse = dataA["data"], !aResponse.isEmpty else { throw NSError(domain: "APIError", code: -4, userInfo: [NSLocalizedDescriptionKey: "API A返回数据缺失"]) } let dataB = try await callBFunction(aResponseString: aResponse) guard let bResponse = dataB["data"], !bResponse.isEmpty else { throw NSError(domain: "APIError", code: -5, userInfo: [NSLocalizedDescriptionKey: "API B返回数据缺失"]) } let dataC = try await callCFunction(bResponseString: bResponse) // 后续加API D、E、F直接按顺序写就行,逻辑一目了然 print("最终结果:\(dataC)") } catch { // 所有错误统一在这里处理 print("请求出错:\(error.localizedDescription)") } } }
优势:代码逻辑和同步代码几乎一致,可读性极强,错误处理集中,新增API成本极低,完全符合Swift现代语法规范。
方案2:使用Promise库(比如PromiseKit,兼容老Swift版本)
如果你的项目还没升级到支持async/await的Swift版本,Promise库是非常成熟的替代方案。以PromiseKit为例,先把原来的回调函数包装成Promise:
import PromiseKit // 包装API A为Promise func callAFunctionPromise() -> Promise<[String: String]> { return Promise { seal in self.callAFunction(completionHandler: { isSuccess, result, errorMsg in if isSuccess { seal.fulfill(result) } else { seal.reject(NSError(domain: "APIError", code: -1, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } } // 包装API B func callBFunctionPromise(aResponseString: String) -> Promise<[String: String]> { return Promise { seal in self.callBFunction(aResponseSting: aResponseString, completionHandler: { isSuccess, result, errorMsg in if isSuccess { seal.fulfill(result) } else { seal.reject(NSError(domain: "APIError", code: -2, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } } // 包装API C func callCFunctionPromise(bResponseString: String) -> Promise<[String: String]> { return Promise { seal in self.callCFunction(bResponseSting: bResponseString, completionHandler: { isSuccess, result, errorMsg in if isSuccess { seal.fulfill(result) } else { seal.reject(NSError(domain: "APIError", code: -3, userInfo: [NSLocalizedDescriptionKey: errorMsg])) } }) } }
调用时用链式的then方法,替代嵌套回调:
func testAPI() { callAFunctionPromise() .then { dataA -> Promise<[String: String]> in guard let aResponse = dataA["data"], !aResponse.isEmpty else { throw NSError(domain: "APIError", code: -4, userInfo: [NSLocalizedDescriptionKey: "API A返回数据缺失"]) } return callBFunctionPromise(aResponseString: aResponse) } .then { dataB -> Promise<[String: String]> in guard let bResponse = dataB["data"], !bResponse.isEmpty else { throw NSError(domain: "APIError", code: -5, userInfo: [NSLocalizedDescriptionKey: "API B返回数据缺失"]) } return callCFunctionPromise(bResponseString: bResponse) } .done { dataC in // 处理最终结果 print("最终结果:\(dataC)") } .catch { error in // 统一处理所有错误 print("请求出错:\(error.localizedDescription)") } }
优势:把嵌套回调转换成链式调用,代码结构清晰,错误处理集中,社区生态成熟,适合老项目过渡。
方案3:DispatchGroup+串行队列(无依赖临时优化)
如果不想用新语法或第三方库,也可以用Swift原生的DispatchGroup来控制API调用顺序,避免嵌套:
// 创建串行队列保证API顺序执行 let apiSerialQueue = DispatchQueue(label: "com.yourcompany.api.serial") func testAPI() { apiSerialQueue.async { var aResponse: String? let groupA = DispatchGroup() groupA.enter() // 调用API A self.callAFunction { isSuccess, dataA, errorMsg in if isSuccess { aResponse = dataA["data"] ?? "" } groupA.leave() } groupA.wait() // 等待API A完成 guard let validAResponse = aResponse, !validAResponse.isEmpty else { print("API A失败或数据缺失") return } var bResponse: String? let groupB = DispatchGroup() groupB.enter() // 调用API B self.callBFunction(aResponseSting: validAResponse) { isSuccess, dataB, errorMsg in if isSuccess { bResponse = dataB["data"] ?? "" } groupB.leave() } groupB.wait() // 等待API B完成 guard let validBResponse = bResponse, !validBResponse.isEmpty else { print("API B失败或数据缺失") return } let groupC = DispatchGroup() groupC.enter() // 调用API C self.callCFunction(bResponseSting: validBResponse) { isSuccess, dataC, errorMsg in if isSuccess { print("最终结果:\(dataC)") } else { print("API C失败:\(errorMsg)") } groupC.leave() } groupC.wait() } }
注意:这种方式虽然避免了嵌套,但代码还是比较繁琐,而且wait()会阻塞当前队列,不适合在主线程使用,只推荐作为临时过渡方案。
内容的提问来源于stack exchange,提问作者Beginnerrrrrr
相关产品推荐
相关产品推荐

