如何避免回调地狱;多个并发任务完成后处理结果

如何避免回调地狱? 

在 Swift 中,回调地狱(callback hell)指的是当你使用嵌套的回调函数时,代码层层嵌套、难以维护和理解。回调地狱通常发生在处理异步操作时,特别是在需要依次执行多个异步任务时,代码的可读性和可维护性会急剧下降。

为了避免回调地狱,Swift 提供了几种解决方案和最佳实践,主要包括以下几种方式:

1. 使用闭包(Closure)链式调用

链式调用是一种将多个异步操作串联起来的方式。你可以在每个操作完成后调用下一个操作的闭包,从而避免嵌套过深。

示例:
func fetchDataFromServer(completion: @escaping (Data?) -> Void) { 
// 异步请求,模拟从服务器获取数据 
    DispatchQueue.global().async { 
        sleep(2)
        let data = Data() 
        // 假设我们得到了数据 
        completion(data) 
    }
 } 

func processData(data: Data, completion: @escaping (ProcessedData) -> Void) {
     DispatchQueue.global().async { 
        sleep(2) 
        let processedData = ProcessedData() 
        // 假设我们处理了数据 
        completion(processedData) 
    } 
} 

func saveData(processedData: ProcessedData, completion: @escaping (Bool) -> Void) {         
    DispatchQueue.global().async { 
        sleep(2) 
        let success = true 
        // 假设保存成功 
        completion(success) 
    } 
} 

// 链式调用,避免回调地狱 
fetchDataFromServer { 
    data in guard let data = data else { return } 
    processData(data: data) { processedData in 
        saveData(processedData: processedData) { success in 
            if success { 
                print("Data saved successfully.") 
            } else 
            { print("Data saving failed.") 
            } 
        }
     }
 }

问题:随着回调的层数增加,代码变得越来越难以阅读和管理。


2. 使用 DispatchGroup

DispatchGroup 允许你等待一组异步任务完成,然后继续执行。它可以帮助避免回调地狱,尤其是在多个异步任务并行执行时。

 dispatchGroup.enter()

dispatchGroup.leave()

dispatchGroup.wait()关键

示例:
class ProcessedData {
        var data: String? = "ProcessedData"
    }
    let queue = DispatchQueue.init(label: "SwiftTest")
    func test(){
        performTasks()
    }
    
    func fetchDataFromServer(completion: @escaping (String?) -> Void) {
        queue.async {
            print("fetchDataFromServer--start")
            sleep(2)
            let data = "fetchDataFromServer"
            completion(data)
            print("fetchDataFromServer")
        }
    }

    func processData(data: String, completion: @escaping (ProcessedData) -> Void) {
        queue.async {
            print("processData--start")
            sleep(2)
            let processedData = ProcessedData()
            completion(processedData)
            print("processData--end")
        }
    }

    func saveData(processedData: ProcessedData, completion: @escaping (Bool) -> Void) {
         queue.async {
             print("saveData--start")
            sleep(2)
            let success = true
            completion(success)
             print("saveData--end")
        }
    }

    func performTasks() {
        let dispatchGroup = DispatchGroup()
        var data: String?
        var processedData: ProcessedData?
        var saveSuccess = false
        dispatchGroup.enter() // 进入第一个任务
        print("performTasks--0")
        fetchDataFromServer { result in
            data = result
            print("performTasks--1")
            dispatchGroup.leave() // 完成任务,退出
        }
        print("performTasks--2")
        dispatchGroup.wait()
        dispatchGroup.enter()
        self.processData(data: data!) { result in
            processedData = result
            print("performTasks--5")
            dispatchGroup.leave() // 完成任务,退出
        }
        
        print("performTasks--6")
        dispatchGroup.wait()
        dispatchGroup.enter() // 进入第三个任务
        self.saveData(processedData: processedData!) { success in
            print("performTasks--8")
            saveSuccess = success
            dispatchGroup.leave() // 完成任务,退出
        }
        dispatchGroup.notify(queue: queue) {
            print("notify--6")
            if let processedData = processedData {
                print("performTasks--7")
                self.saveData(processedData: processedData) { success in
                    print("performTasks--8")
                    saveSuccess = success
//                    dispatchGroup.leave() // 完成任务,退出
                }
            }
        }
        dispatchGroup.notify(queue: .main) {
            if saveSuccess {
                print("Data saved successfully.")
            } else {
                print("Data saving failed.")
            }
        }
        
        DispatchQueue.global().asyncAfter(deadline: .now() + 200.0) {
            print("打印完成")
        }
        sleep(200)
    }

3. 使用 async / await(Swift 5.5+)

Swift 5.5 引入了原生的异步支持,通过 async 和 await 关键字来简化异步代码的编写。使用 async/await 可以让异步操作看起来像是同步操作,从而避免了回调地狱的问题。

示例:
 

swift

复制代码

func fetchDataFromServer() async -> Data? { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return Data() } func processData(data: Data) async -> ProcessedData { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return ProcessedData() } func saveData(processedData: ProcessedData) async -> Bool { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return true } func performTasks() async { if let data = await fetchDataFromServer() { let processedData = await processData(data: data) let success = await saveData(processedData: processedData) if success { print("Data saved successfully.") } else { print("Data saving failed.") } } } // 调用异步任务 Task { await performTasks() }

4. 使用 Combine

Combine 是 Apple 的响应式编程框架,它可以让你更优雅地处理多个异步操作,避免回调地狱。通过 Combine,你可以将异步操作组合成数据流,并使用操作符(如 flatMapmerge 等)来处理和转换数据。

示例:
 

swift

复制代码

import Combine func fetchDataFromServer() -> AnyPublisher<Data, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(Data())) } } .eraseToAnyPublisher() } func processData(data: Data) -> AnyPublisher<ProcessedData, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(ProcessedData())) } } .eraseToAnyPublisher() } func saveData(processedData: ProcessedData) -> AnyPublisher<Bool, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(true)) } } .eraseToAnyPublisher() } func performTasks() { let cancellable = fetchDataFromServer() .flatMap { data in processData(data: data) } .flatMap { processedData in saveData(processedData: processedData) } .sink { success in if success { print("Data saved successfully.") } else { print("Data saving failed.") } } } // 调用 Combine 操作 performTasks()

5. 使用 Result 类型

如果你希望异步任务的回调能返回成功与失败的结果,Result 类型是一个有用的工具。你可以通过封装每个任务的结果来避免深层的回调嵌套。

示例:
 

swift

复制代码

enum TaskError: Error { case dataFetchError } func fetchData() async -> Result<Data, TaskError> { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return .success(Data()) } func processData(data: Data) async -> Result<ProcessedData, TaskError> { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return .success(ProcessedData()) } func saveData(processedData: ProcessedData) async -> Result<Bool, TaskError> { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return .success(true) } func performTasks() async { switch await fetchData() { case .success(let data): switch await processData(data: data) { case .success(let processedData): switch await saveData(processedData: processedData) { case .success(let success): if success { print("Data saved successfully.") } else { print("Data saving failed.") } case .failure: print("Error saving data.") } case .failure: print("Error processing data.") } case .failure: print("Error fetching data.") } } Task { await performTasks() }

6.使用RXSwift 

1. 为什么要使用 RxSwift? · RxSwift 中文文档

总结

要避免回调地狱,主要可以通过以下几种方式来优化异步代码:

  1. 闭包链式调用:通过在闭包中执行下一步操作来避免回调地狱。
  2. DispatchGroup:通过 DispatchGroup 来并行执行多个异步任务并等待它们的完成。
  3. async/await:在 Swift 5.5 引入的 async/await 特性让异步编程变得像同步编程一样简单。
  4. Combine:利用 Combine 框架的操作符来处理多个异步任务,避免深层嵌套。
  5. Result 类型:封装每个异步任务的结果,以便优雅地处理错误。

这些方法不仅能提高代码的可读性和可维护性,还能让你以更加现代化的方式进行异步编程。

多个并发任务完成后处理结果 

在 iOS 开发中,等待多个并发任务完成并处理结果是一个常见的需求。你可以使用多种方法来实现这一功能,以下是几种常见的实现方式:

1. 使用 DispatchGroup

DispatchGroup 是一种非常常见的方法,它可以让你等待一组异步任务完成,然后进行后续处理。你可以使用 enter() 和 leave() 来标记任务的开始和结束,使用 notify() 来在所有任务完成后处理结果。

示例:
 

swift

复制代码

func fetchDataFromServer(completion: @escaping (Data?) -> Void) { DispatchQueue.global().async { sleep(2) // 模拟网络请求延迟 let data = Data() // 假设获取到的数据 completion(data) } } func processData(data: Data, completion: @escaping (ProcessedData?) -> Void) { DispatchQueue.global().async { sleep(2) // 模拟数据处理延迟 let processedData = ProcessedData() // 假设处理结果 completion(processedData) } } func saveData(processedData: ProcessedData, completion: @escaping (Bool) -> Void) { DispatchQueue.global().async { sleep(2) // 模拟保存数据延迟 let success = true // 假设保存成功 completion(success) } } func performTasks() { let dispatchGroup = DispatchGroup() var data: Data? var processedData: ProcessedData? var saveSuccess: Bool? // 第一个任务:获取数据 dispatchGroup.enter() fetchDataFromServer { result in data = result dispatchGroup.leave() } // 第二个任务:处理数据 dispatchGroup.enter() dispatchGroup.notify(queue: .global()) { // 确保第二个任务在第一个任务完成后执行 if let data = data { processData(data: data) { result in processedData = result dispatchGroup.leave() } } } // 第三个任务:保存数据 dispatchGroup.enter() dispatchGroup.notify(queue: .global()) { // 确保第三个任务在第二个任务完成后执行 if let processedData = processedData { saveData(processedData: processedData) { success in saveSuccess = success dispatchGroup.leave() } } } // 当所有任务都完成后,执行最终的处理 dispatchGroup.notify(queue: .main) { if let success = saveSuccess, success { print("All tasks completed successfully.") } else { print("One or more tasks failed.") } } } performTasks()

2. 使用 async/await(Swift 5.5+)

从 Swift 5.5 开始,Apple 引入了 async/await 语法,这使得异步编程变得更加简洁和易于理解。你可以利用 async/await 来等待多个并发任务完成,并在所有任务完成后处理结果。

示例:
 

swift

复制代码

func fetchDataFromServer() async -> Data? { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return Data() } func processData(data: Data) async -> ProcessedData? { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return ProcessedData() } func saveData(processedData: ProcessedData) async -> Bool { await Task.sleep(2 * 1_000_000_000) // 模拟延迟 return true } func performTasks() async { async let data = fetchDataFromServer() async let processedData = processData(data: await data) async let success = saveData(processedData: await processedData) // 等待所有任务完成并处理结果 let saveSuccess = await success if saveSuccess { print("All tasks completed successfully.") } else { print("One or more tasks failed.") } } Task { await performTasks() }

3. 使用 Combine

Combine 是 Apple 提供的响应式编程框架,它也非常适合处理多个并发任务。在 Combine 中,可以使用 Publisher 来表示异步任务,并使用操作符如 combineLatestzip 或 flatMap 来组合多个异步任务。

示例:
 

swift

复制代码

import Combine func fetchDataFromServer() -> AnyPublisher<Data, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(Data())) // 模拟数据获取 } } .eraseToAnyPublisher() } func processData(data: Data) -> AnyPublisher<ProcessedData, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(ProcessedData())) // 模拟数据处理 } } .eraseToAnyPublisher() } func saveData(processedData: ProcessedData) -> AnyPublisher<Bool, Never> { return Future { promise in DispatchQueue.global().asyncAfter(deadline: .now() + 2) { promise(.success(true)) // 模拟数据保存 } } .eraseToAnyPublisher() } func performTasks() { let cancellable = fetchDataFromServer() .flatMap { data in processData(data: data) } .flatMap { processedData in saveData(processedData: processedData) } .sink { success in if success { print("All tasks completed successfully.") } else { print("One or more tasks failed.") } } } performTasks()

4. 使用 OperationQueue(验证结论失败,不能保证顺序)

OperationQueue 是一个用于并发任务管理的类,可以使用它来执行多个任务,并在所有任务完成后处理结果。OperationQueue 允许你控制任务的优先级、最大并发数等。

示例:
 

swift

复制代码

func fetchDataFromServer(completion: @escaping (Data?) -> Void) { // 模拟延迟 DispatchQueue.global().asyncAfter(deadline: .now() + 2) { completion(Data()) } } func processData(data: Data, completion: @escaping (ProcessedData?) -> Void) { DispatchQueue.global().asyncAfter(deadline: .now() + 2) { completion(ProcessedData()) } } func saveData(processedData: ProcessedData, completion: @escaping (Bool) -> Void) { DispatchQueue.global().asyncAfter(deadline: .now() + 2) { completion(true) } } func performTasks() { let operationQueue = OperationQueue() var data: Data? var processedData: ProcessedData? var saveSuccess: Bool? let fetchOperation = BlockOperation { fetchDataFromServer { result in data = result } } let processOperation = BlockOperation { if let data = data { processData(data: data) { result in processedData = result } } } let saveOperation = BlockOperation { if let processedData = processedData { saveData(processedData: processedData) { success in saveSuccess = success } } } saveOperation.addDependency(processOperation) // 确保保存操作在处理数据后执行 processOperation.addDependency(fetchOperation) // 确保处理数据操作在获取数据后执行 operationQueue.addOperations([fetchOperation, processOperation, saveOperation], waitUntilFinished: false) operationQueue.addOperation { if let success = saveSuccess, success { print("All tasks completed successfully.") } else { print("One or more tasks failed.") } } } performTasks()

总结

  1. DispatchGroup:适用于等待多个并发任务完成后进行处理,使用 enter()leave() 和 notify()
  2. async/await:适用于 Swift 5.5 及以上版本,提供更简洁的异步编程方式,避免回调地狱。
  3. Combine:适用于响应式编程,通过 Publisher 和操作符组合多个异步任务。
  4. OperationQueue:适用于对多个并发任务进行细粒度的控制,例如控制任务的优先级、最大并发数等。

根据项目需求和团队的技术栈,选择适合的异步任务管理方式。如果你使用的是 Swift 5.5+,推荐使用 async/await,因为它最简洁、易懂。对于响应式编程需求较强的项目,可以考虑 Combine

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值