Swift并发:如何让任务等待特定数据加载完成后执行?
解决方案:用Actor实现Token刷新的串行化与请求等待
完全可以实现你的需求,Actor是Swift并发中解决这类共享资源同步、避免重复操作的最佳方案,它天然保证所有方法调用串行执行,同时可以维护一个正在进行的Token刷新任务引用,让后续请求自动等待刷新完成后再执行。
改造后的代码实现
actor APIManager { // 跟踪正在进行的Token刷新任务,避免重复触发 private var tokenRenewalTask: Task<Void, Error>? func execute<R: ResponseProtocol>(request: RequestProtocol, response: R) async throws -> R.Value { let session = getURLSession() var urlRequest: URLRequest = request.buildURLRequest() let (data, urlResponse) = try await session.data(for: urlRequest) if let httpURLResponse = urlResponse as? HTTPURLResponse, httpURLResponse.statusCode == 401 { try await handleTokenRenewal() // 刷新Token后,重新构建带新Token的请求并重试 urlRequest = request.buildURLRequest() let (retryData, _) = try await session.data(for: urlRequest) return try decode(data: retryData) } return try decode(data: data) } /// 处理Token刷新逻辑,确保同一时间只有一个刷新任务在执行 private func handleTokenRenewal() async throws { if let existingTask = tokenRenewalTask { // 已有刷新任务,等待其完成 try await existingTask.value } else { // 没有正在进行的刷新任务,创建新任务 let task = Task { try await renewBearerToken() } tokenRenewalTask = task do { try await task.value } catch { // 刷新失败,抛出错误并清空任务引用 tokenRenewalTask = nil throw error } // 刷新成功,清空任务引用 tokenRenewalTask = nil } } // 原有的辅助方法保持不变 private func getURLSession() -> URLSession { // 返回你的URLSession实例 URLSession.shared } private func renewBearerToken() async throws { // 实现Token刷新逻辑 } private func decode<R: ResponseProtocol>(data: Data) throws -> R.Value { // 实现数据解码逻辑 try JSONDecoder().decode(R.Value.self, from: data) } }
关键逻辑说明
- Actor改造:将
APIManager从class改为actor,确保所有异步方法调用都是串行执行的,避免多Task同时修改共享状态(比如tokenRenewalTask)。 - 刷新任务跟踪:通过
tokenRenewalTask变量记录当前是否有正在进行的Token刷新操作:- 当第一个请求遇到401时,创建刷新任务并执行;
- 后续请求遇到401时,发现已有刷新任务,直接等待该任务完成,无需重复发起刷新。
- 错误处理:如果Token刷新失败,会抛出错误并清空任务引用,避免后续请求一直等待无效任务。
- 重试请求:刷新Token后,需要重新构建带新Token的请求(因为原请求的Token已经失效),再发起重试。
为什么不用TaskGroups?
TaskGroups的核心用途是并发执行多个子任务并汇总结果,而你的场景需要的是串行化关键操作(Token刷新)+ 让其他请求等待该操作完成,Actor的串行执行模型更贴合这个需求,代码也更简洁易维护。
内容的提问来源于stack exchange,提问作者JorisT
相关产品推荐
相关产品推荐

