我正在研究 Swift 中可取消的异步延迟,它向后兼容 iOS 15:
Task {
let before = Date.timeIntervalSinceReferenceDate
let seconds: TimeInterval = 100
print("> sleeping for \(seconds) seconds...")
try await Task.sleep(nanoseconds: UInt64(seconds * 1_000_000_000))
let after = Date.timeIntervalSinceReferenceDate
print(String(format: "> slept for %0.3f seconds", after - before))
}
使用
Task.sleep
的解决方案效果很好,但对于较长的延迟来说似乎相当不准确。
在 iOS 16 模拟器上运行会产生以下结果:
> sleeping for 20.0 seconds...
> slept for 21.332 seconds
> sleeping for 60.0 seconds...
> slept for 63.995 seconds
> sleeping for 100.0 seconds...
> slept for 106.662 seconds
有没有更好的方法来实现这个目标?
func accurateDelay(seconds: TimeInterval) async {
await withCheckedContinuation { continuation in
let queue = DispatchQueue.global(qos: .default)
let timer = DispatchSource.makeTimerSource(queue: queue)
timer.schedule(deadline: .now() + seconds, leeway: .nanoseconds(1))
timer.setEventHandler {
continuation.resume()
timer.cancel()
}
timer.activate()
}
}
我认为 A
DispatchSourceTimer
可以用来更准确地执行某些操作。下面的例子给了我一些非常准确的信息。
struct AccurateTimerView: View {
let delay: TimeInterval
var body: some View {
Button(action: {
Task {
let before = Date.timeIntervalSinceReferenceDate
await accurateDelay(seconds: delay)
let after = Date.timeIntervalSinceReferenceDate
print(String(format: "> slept for %0.3f seconds", after - before))
}
}, label: {
Text("Start")
})
}
}
#Preview {
VStack {
AccurateTimerView(delay: 20)
AccurateTimerView(delay: 60)
AccurateTimerView(delay: 100)
}
}
> slept for 20.001 seconds
> slept for 60.000 seconds
> slept for 99.999 seconds