Search code examples
iosswiftgrand-central-dispatch

Why are in DispatchQueue.main.asyncAfter(..., operations with different delay executing at same time


my code:

let operationStart = Date()

for i in 0...20 {

    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5 * Double(i)) {

        let distance = operationStart.distance(to: Date())

        print("operation \(i) executed after \(distance)")
    }

}

Output:

operation 0 executed after 0.001116037368774414
operation 1 executed after 0.5226989984512329
operation 2 executed after 1.0015490055084229

...

operation 9 executed after 4.940060019493103
operation 10 executed after 5.499879002571106
operation 11 executed after 6.049391031265259
operation 12 executed after 6.0494760274887085
operation 13 executed after 7.105716943740845
operation 14 executed after 7.105777978897095
operation 15 executed after 8.189378023147583
operation 16 executed after 8.189532995223999
operation 17 executed after 9.30673599243164
operation 18 executed after 9.306856036186218
operation 19 executed after 10.423419952392578
operation 20 executed after 10.423549056053162

how to avoid this? I need to execute operations with equal delays.


Solution

  • Use DispatchSourceTimer for this purpose because if you have a bunch of asyncAfter calls in the future). When its coalescing, the overall speed should remain largely consistent, but it will coalesce them together, giving you more stuttering in your timers

       let operationStart = Date()
        let queue = DispatchQueue(label: "com.domain.app.timer")
        var timer = DispatchSource.makeTimerSource(queue: queue)
        var i = 0
        //for i in 0...20 {
        timer.schedule(deadline: .now(), repeating: 0.5, leeway: .seconds(0))
        timer.setEventHandler {  // `[weak self]` only needed if you reference `self` in this closure and you want to prevent strong reference cycle
            let distance = operationStart.distance(to: Date())
            i = i+1
            if i == 20 {
                timer.cancel()
            }
            print("operation \(i) executed after \(distance)")
        }
    
        timer.resume()
    

    enter image description here