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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 21:07:42