Swift并发中Promise-Resolver模式的惯用Async/Await实现方案
用Swift Async/Await替代PromiseKit的单次解析Promise模式
原PromiseKit实现回顾
在PromiseKit中,我们可以创建一对Promise和解析器(Seal),用于表示一个后续会被解析的异步状态:
class ExampleClass { // 存储用于获取头条新闻的Promise和解析器 private let (headlinePromise, headlineSeal) = Promise<String>.pending() }
可以通过Promise链式调用等待状态就绪:
headlinePromise.get { headline in updateUI(headline: headline) } // 立即执行后续代码,直到Promise被解析才会调用updateUI
通过解析器触发状态完成:
makeNetworkRequest("https://news.example/headline").get { headline in headlineSeal.fulfill(headline) }
一旦Promise被解析,所有等待它的代码会立即执行,且Promise只能被解析一次,后续的解析操作无效。
Swift Async/Await的地道实现
我们可以借助CheckedThrowingContinuation(或CheckedContinuation,用于无错误场景)结合Task来实现等价逻辑,完全满足要求的特性:
class ExampleClass { // 存储用于触发异步状态完成的continuation,仅能被调用一次 private var headlineContinuation: CheckedContinuation<String, Error>? // 封装异步状态的Task,初始化时处于挂起状态 private let headlineTask: Task<String, Error> init() { headlineTask = Task { // 挂起Task,等待外部通过continuation触发完成 try await withCheckedThrowingContinuation { continuation in headlineContinuation = continuation } } } // 等待异步状态就绪并获取值 func awaitHeadline() async throws -> String { try await headlineTask.value } // 标记状态为成功完成,传入结果值 func completeHeadline(with value: String) { headlineContinuation?.resume(returning: value) headlineContinuation = nil // 置空避免重复调用 } // 标记状态为失败,传入错误 func failHeadline(with error: Error) { headlineContinuation?.resume(throwing: error) headlineContinuation = nil // 置空避免重复调用 } // 模拟网络请求方法 private func makeNetworkRequest(_ url: String) async throws -> String { // 实际网络请求逻辑 return "Swift并发:用Continuation替代PromiseKit" } // 示例:触发网络请求并完成状态 func fetchAndCompleteHeadline() async { do { let headline = try await makeNetworkRequest("https://news.example/headline") completeHeadline(with: headline) } catch { failHeadline(with: error) } } // 示例:等待状态并更新UI func waitForHeadlineAndUpdateUI() async throws { let headline = try await awaitHeadline() updateUI(headline: headline) } private func updateUI(headline: String) { // UI更新逻辑 print(headline) } }
特性匹配说明
- 声明依赖并等待状态:通过调用
try await awaitHeadline(),代码会挂起直到状态被完成,无论在实例的哪个方法中都可以使用。 - 任意实例方法完成状态:
completeHeadline(with:)和failHeadline(with:)(或者fetchAndCompleteHeadline()这类业务方法)都可以触发状态完成,完全不受调用位置限制。 - 状态就绪后后续代码立即执行:一旦
continuation被resume,所有等待headlineTask.value的代码会立刻恢复执行;后续再调用awaitHeadline()会直接返回已缓存的结果,无需等待。 - 状态不可变且无法重复修改:
headlineContinuation在触发后会被置空,无法再次调用resume;同时Task的结果是只读且缓存的,一旦完成就无法修改状态。
内容的提问来源于stack exchange,提问作者bdesham
相关产品推荐
相关产品推荐

