gpt4 book ai didi

swift - 如何将 DispatchQueue 去抖动转换为 Swift 并发任务?

转载 作者:行者123 更新时间:2023-12-04 12:52:57 24 4
gpt4 key购买 nike

我有一个使用 DispatchQueue 的现有 debouncer 实用程序.它接受一个闭包并在达到时间阈值之前执行它。它可以像这样使用:

let limiter = Debouncer(limit: 5)
var value = ""

func sendToServer() {
limiter.execute {
print("\(Date.now.timeIntervalSince1970): Fire! \(value)")
}
}

value.append("h")
sendToServer() // Waits until 5 seconds
value.append("e")
sendToServer() // Waits until 5 seconds
value.append("l")
sendToServer() // Waits until 5 seconds
value.append("l")
sendToServer() // Waits until 5 seconds
value.append("o")
sendToServer() // Waits until 5 seconds
print("\(Date.now.timeIntervalSince1970): Last operation called")

// 1635691696.482115: Last operation called
// 1635691701.859087: Fire! hello
请注意,它不是在调用 Fire!多次,但仅在上次使用上次任务的值后 5 秒。 Debouncer实例配置为无论调用多少次,都将队列中的最后一个任务保留 5 秒。闭包传递到 execute(block:)方法:
final class Debouncer {
private let limit: TimeInterval
private let queue: DispatchQueue
private var workItem: DispatchWorkItem?
private let syncQueue = DispatchQueue(label: "Debouncer", attributes: [])

init(limit: TimeInterval, queue: DispatchQueue = .main) {
self.limit = limit
self.queue = queue
}

@objc func execute(block: @escaping () -> Void) {
syncQueue.async { [weak self] in
if let workItem = self?.workItem {
workItem.cancel()
self?.workItem = nil
}

guard let queue = self?.queue, let limit = self?.limit else { return }

let workItem = DispatchWorkItem(block: block)
queue.asyncAfter(deadline: .now() + limit, execute: workItem)

self?.workItem = workItem
}
}
}
如何将其转换为并发操作,以便可以像下面这样调用它:
let limit = Debouncer(limit: 5)

func sendToServer() {
await limiter.waitUntilFinished
print("\(Date.now.timeIntervalSince1970): Fire! \(value)")
}

sendToServer()
sendToServer()
sendToServer()
但是,这不会使任务去抖动,而是将它们挂起,直到下一个被调用。相反,它应该取消前一个任务并保持当前任务直到去抖动时间。这可以通过 Swift Concurrency 来完成吗?还是有更好的方法来做到这一点?

最佳答案

任务有能力使用isCancelledcheckCancellation ,但是为了去抖动例程,你想等待一段时间,你可能只使用 Task.sleep(nanoseconds:) 的 throw 再现。 ,其 documentation说:

If the task is canceled before the time ends, this function throws CancellationError.


因此,这有效地消除了 2 秒的抖动。
var task: Task<(), Never>?

func debounced(_ string: String) {
task?.cancel()

task = Task {
do {
try await Task.sleep(nanoseconds: 2_000_000_000)
logger.log("result \(string)")
} catch {
logger.log("canceled \(string)")
}
}
}
(为什么 Apple 恢复到纳秒级是我无法理解的。)
请注意, sleep(nanoseconds:) 的非 throw 演绎不检测取消,因此您必须使用此 throw 再现。

关于swift - 如何将 DispatchQueue 去抖动转换为 Swift 并发任务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69787568/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com