如何将DispatchQueue实现的防抖逻辑转换为Swift Concurrency任务?
这个需求完全可以通过Swift Concurrency实现,核心思路是利用Task的取消机制替代原有DispatchWorkItem的取消逻辑,同时借助Actor保证状态操作的线程安全,不需要手动维护同步队列。
实现代码
actor Debouncer { private let limit: TimeInterval private var currentTask: Task<Void, Never>? init(limit: TimeInterval) { self.limit = limit } /// 提交防抖任务 /// - Parameter operation: 达到防抖阈值后要执行的操作 func execute(_ operation: @escaping () async -> Void) { // 取消前一次的待执行任务 currentTask?.cancel() // 新建当前任务 let task = Task { // 等待防抖阈值时间,任务被取消会直接抛出中断 try? await Task.sleep(nanoseconds: UInt64(limit * 1_000_000_000)) // 确认任务未被取消再执行操作 guard !Task.isCancelled else { return } await operation() } currentTask = task } /// 等待当前防抖任务执行完成(可选实现,按需使用) func waitUntilFinished() async { await currentTask?.value } }
使用示例
使用方式和原有GCD版本逻辑一致,运行效果完全相同:
let limiter = Debouncer(limit: 5) var value = "" func sendToServer() async { await limiter.execute { // 如果需要指定在主队列执行,可在此处加上 @MainActor 标记 print("\(Date.now.timeIntervalSince1970): Fire! \(value)") } } value.append("h") await sendToServer() value.append("e") await sendToServer() value.append("l") await sendToServer() value.append("l") await sendToServer() value.append("o") await sendToServer() print("\(Date.now.timeIntervalSince1970): Last operation called") // 输出效果: // 1635691696.482115: Last operation called // 1635691701.859087: Fire! hello
原有写法的问题说明
你之前尝试的写法把业务逻辑放在await limiter.waitUntilFinished之后,相当于每次调用都会生成独立的执行逻辑,只是挂起等待前序任务结束,没有取消前序任务的逻辑,自然不会产生防抖效果。将业务逻辑传入execute方法内部,由Debouncer统一管控任务的取消、延时、触发流程,才能达到预期的防抖效果。
方案优势
相比原有GCD实现,这个版本不需要手动维护同步队列做状态同步,线程安全由Swift Concurrency系统自动保证,代码更简洁,出错概率更低。如果需要扩展节流、立即执行、指定执行队列等特性,直接在现有实现基础上增加参数即可。
内容的提问来源于stack exchange,提问作者TruMan1
相关产品推荐
相关产品推荐

