如何在Swift 4中模拟JavaScript的async/await?DispatchQueue用法解惑
在Swift 4中模拟JavaScript的async/await逻辑(基于DispatchQueue)
嘿,我懂你想在Swift 4里复刻JavaScript那种清爽的async/await写法的需求!毕竟Swift 5.5才加入原生的async/await语法,在Swift 4里确实得靠DispatchQueue(也就是GCD,Grand Central Dispatch)来模拟类似的异步流程控制。我给你拆解下具体怎么操作,还有背后的基本逻辑~
先搞懂DispatchQueue的核心逻辑
简单来说,DispatchQueue就是苹果用来管理任务执行的“调度器”:你可以把任务(比如网络请求、耗时计算)丢到不同的队列里,GCD会自动帮你把这些任务分配到合适的线程上运行——不管是同步(等当前任务做完再执行下一个)还是异步(不用等,直接丢去后台跑)。我们要模拟async/await,核心就是用GCD的工具来控制异步任务的执行顺序,以及等待任务完成的时机。
模拟async/await的两种常用方式
首先先定义一个模拟异步请求的基础函数(比如网络调用或者耗时操作):
// 模拟异步请求:比如网络接口调用、本地文件读取 func fetchMockData(completion: @escaping (String?, Error?) -> Void) { // 把任务丢到全局后台队列异步执行,不阻塞当前线程 DispatchQueue.global().async { // 模拟耗时操作(比如网络请求等待2秒) Thread.sleep(forTimeInterval: 2) // 模拟请求结果,你可以替换成真实业务逻辑 let requestSuccess = true if requestSuccess { completion("从服务器拿到的模拟数据", nil) } else { completion(nil, NSError(domain: "com.example.app", code: 1001, userInfo: [NSLocalizedDescriptionKey: "请求失败"])) } } }
1. 用DispatchGroup等待多个异步任务完成(类似JS的Promise.all)
如果你需要同时启动多个异步任务,等所有任务都完成后再统一处理结果,就可以用DispatchGroup来实现类似await Promise.all()的效果:
func simulateAsyncAllTasks() { let taskGroup = DispatchGroup() print("开始执行所有异步任务...") // 第一个异步任务 taskGroup.enter() // 标记任务开始 fetchMockData { data, error in defer { taskGroup.leave() } // 任务结束时标记离开 if let data = data { print("任务1完成:\(data)") } else if let error = error { print("任务1出错:\(error.localizedDescription)") } } // 第二个异步任务 taskGroup.enter() fetchMockData { data, error in defer { taskGroup.leave() } if let data = data { print("任务2完成:\(data)") } else if let error = error { print("任务2出错:\(error.localizedDescription)") } } // 所有任务完成后执行的回调,完全不会阻塞线程(推荐用这个,别用wait()) taskGroup.notify(queue: DispatchQueue.main) { print("✅ 所有异步任务都完成了!可以在这里处理最终结果、更新UI") } }
逻辑说明:
enter()相当于给任务“打卡”,告诉Group有一个任务要开始了leave()是任务“下班打卡”,告诉Group这个任务结束了notify()会在所有任务都完成(所有enter()都对应了leave())时,在指定队列(比如主线程)执行回调,完全不会阻塞当前线程。
2. 用DispatchSemaphore实现顺序异步任务(类似JS的await串行执行)
如果需要按顺序执行异步任务(比如先请求A,拿到A的结果再请求B),可以用DispatchSemaphore来模拟await的“等待任务完成再继续”效果:
func simulateAsyncAwaitSerial() { // 把所有阻塞操作放到后台队列,绝对不能在主线程执行! DispatchQueue.global().async { print("开始执行第一个任务") let semaphore1 = DispatchSemaphore(value: 0) var firstTaskResult: String? var firstTaskError: Error? // 执行第一个异步任务 fetchMockData { data, error in firstTaskResult = data firstTaskError = error semaphore1.signal() // 任务完成,给信号量“发信号” } semaphore1.wait() // 等待信号量,直到任务完成(类似await) // 处理第一个任务结果 guard let firstData = firstTaskResult else { print("第一个任务失败:\(firstTaskError?.localizedDescription ?? "未知错误")") return } print("第一个任务完成:\(firstData)") // ------------------------------ // 用第一个任务的结果执行第二个任务 print("开始执行第二个任务") let semaphore2 = DispatchSemaphore(value: 0) var secondTaskResult: String? var secondTaskError: Error? fetchMockData { data, error in secondTaskResult = data secondTaskError = error semaphore2.signal() } semaphore2.wait() // 处理第二个任务结果 if let secondData = secondTaskResult { print("第二个任务完成:\(secondData)") // 回到主线程更新UI DispatchQueue.main.async { print("✅ 所有顺序任务完成,现在可以更新UI啦") } } } }
逻辑说明:
- 信号量初始值设为0,
wait()会阻塞当前线程,直到signal()被调用(信号量+1) - 这样就实现了“等前一个异步任务完成,再执行下一段代码”的效果,和JS里的
await逻辑一致。
重要注意事项
- 绝对不要在主线程调用
wait()或者信号量的wait()!会直接卡死UI,所有阻塞式等待都要放在DispatchQueue.global()这类后台队列里。 - 如果觉得重复写信号量/Group的逻辑太繁琐,可以自己封装一个工具函数,把这些重复代码抽出来,让业务代码更接近async/await的简洁感。
内容的提问来源于stack exchange,提问作者cusmar
相关产品推荐
相关产品推荐

