如何在Swift中利用Async/Await创建HTTP请求任务栈以保证任务执行顺序
用Swift Async/Await实现串行任务队列,保证HTTP请求按用户操作顺序执行
要解决你遇到的任务并行执行、顺序混乱的问题,最直接的方案是创建一个线程安全的串行任务调度器,把所有用户触发的HTTP相关任务都加入这个调度器,让它们按加入顺序依次执行。下面是具体实现步骤:
1. 实现任务调度器(用Actor保证线程安全)
Swift的Actor天生具备线程安全特性,非常适合用来管理串行任务队列。我们可以创建一个TaskScheduler Actor,它负责维护任务队列,并逐个执行任务:
actor TaskScheduler { // 标记当前是否正在处理任务 private var isProcessingTasks = false // 待执行的任务队列 private var pendingTasks: [() async throws -> Void] = [] /// 将任务加入队列,等待串行执行 func enqueue(task: @escaping () async throws -> Void) async throws { pendingTasks.append(task) // 如果当前没有在处理任务,就启动执行流程 if !isProcessingTasks { isProcessingTasks = true defer { isProcessingTasks = false } // 逐个执行队列中的任务 while !pendingTasks.isEmpty { let nextTask = pendingTasks.removeFirst() try await nextTask() } } } /// 获取剩余待执行的任务数量(可选) var remainingTaskCount: Int { pendingTasks.count } }
2. 集成到你的业务流程中
首先创建一个全局的调度器实例(或者根据需求用单例):
// 可以放在全局作用域,或者作为你的ViewModel的属性 let taskScheduler = TaskScheduler()
然后修改你的按钮点击事件,把login任务加入调度器,而不是直接启动新Task:
Button(action: { Task { do { try await taskScheduler.enqueue { try await self.login(username: username, password: password) } } catch { // 统一处理错误,比如弹窗提示 print("任务执行失败:\(error)") } } }, label: { Text("Login") })
3. 为什么这个方案有效?
- 串行执行保证顺序:调度器每次只会执行一个任务,只有当前任务完成(包括内部的所有
await操作),才会执行下一个队列中的任务。不管用户点击多少次按钮,所有任务都会严格按操作顺序发送到服务器。 - 线程安全:Actor的特性保证了
enqueue方法和任务队列的访问都是线程安全的,不需要额外加锁。 - 兼容现有代码:你不需要修改
httpRequest和login的内部逻辑,只需要把任务包装后加入调度器即可。
可选:拆分任务或获取剩余任务数
如果你想把login拆成多个独立任务(比如让登录、获取用户数据、获取通知这些步骤也能和其他用户任务穿插执行),可以把每个步骤单独加入调度器:
func login(username: String, password: String) async throws { // 第一步:登录 try await taskScheduler.enqueue { let dataCredentials = try await self.httpRequest(requestType: .login) let credentials = try await self.decodeData(dataCredentials) // 保存凭证逻辑... } // 第二步:获取用户数据 try await taskScheduler.enqueue { let dataUserdata = try await self.httpRequest(requestType: .requestOwnUserData) let userdata = try await self.decodeData(dataUserdata) // 更新用户数据逻辑... } // 第三步:获取通知 try await taskScheduler.enqueue { let dataNotifications = try await self.httpRequest(requestType: .requestUserNotifications) let notifications = try await self.decodeData(dataNotifications) // 更新通知逻辑... } }
如果需要统计剩余任务数,只需要异步访问调度器的属性:
Task { let count = await taskScheduler.remainingTaskCount print("还有\(count)个任务待执行") }
内容的提问来源于stack exchange,提问作者JacquesNorris
相关产品推荐
相关产品推荐

