Search code examples
swiftmacostimerfoundation

Swift `Timer` firing before the specified time interval


I got a doubt about how Timer.scheduledTimer() fires the block of code if the execution takes more time than the specified withTimeInterval:.

Does the countdown to firing starts after the execution of the block or when the first statement of the block is executed?

So I tested with the following code:

//Logic is to waste the time in the block which will take more than 5 secs to run.
Timer.scheduledTimer(withTimeInterval: 5, repeats: true){
    timer in
    var sum = 0
    var count = 0

    print("START===================================")
    print(Int64(Date().timeIntervalSince1970 * 1000))
    for i in 2..<100000
    {
        for j in 2..<10000
        {
            sum = i+j
        }
    }

    print(sum) // Ignore this. sum is used here so that compiler might won't be able to remove the loop in the optimisations due to unused variable reason.
    print(Int64(Date().timeIntervalSince1970 * 1000))
    print("END===================================")
}

RunLoop.main.run()

Output:

START===================================
1507965166992
109998
1507965173888
END===================================
START===================================
1507965176993
109998
1507965183890
END===================================
START===================================
1507965186989

When I subtracted end-time of previous loop and start-time of current loop, I always get around 3 secs. But I have specified 5 secs. Why is that?


Solution

  • Check the documentation of Timer.

    Overview

    ...

    ...

    A timer is not a real-time mechanism. If a timer’s firing time occurs during a long run loop callout or while the run loop is in a mode that isn't monitoring the timer, the timer doesn't fire until the next time the run loop checks the timer. Therefore, the actual time at which a timer fires can be significantly later.

    ...

    So, the timer doesn't execute your block of code at the exact time always. It schedules it on the run loop. If there are some important threads to execute, then your code will be delayed. So, to maintain a fixed firing time, look at specifying tolerance value.

    and

    Comparing Repeating and Nonrepeating Timers

    You specify whether a timer is repeating or nonrepeating at creation time. A nonrepeating timer fires once and then invalidates itself automatically, thereby preventing the timer from firing again. By contrast, a repeating timer fires and then reschedules itself on the same run loop. A repeating timer always schedules itself based on the scheduled firing time, as opposed to the actual firing time. For example, if a timer is scheduled to fire at a particular time and every 5 seconds after that, the scheduled firing time will always fall on the original 5-second time intervals, even if the actual firing time gets delayed. If the firing time is delayed so far that it passes one or more of the scheduled firing times, the timer is fired only once for that time period; the timer is then rescheduled, after firing, for the next scheduled firing time in the future.

    Here, it says that the countdown starts when the first timer is started but not when the timer has ended.


    For your 1st question:

    Does the countdown to firing starts after the execution of the block or when the first statement of the block is executed?

    Answer: Neither. The countdown starts when the first timer is started.


    For your 2nd question:

    When I subtracted endtime of previous loop and starttime of current loop, I always get around 3 sec. But I have specified 5 sec. Why is that?

    Answer:

    1). As I said in your 1st question, the countdown starts after the first timer has started.

    2). In Comparing Repeating and Nonrepeating Timers, they said that if the logic takes more time than the specified time in the timer then it executes the code only once after the completion of the execution block and also after the specified time.

    i.e.,

    Timer interval: 5
    
    Time for block to complete: 8
    
    Then next code will be executed at: 8 + 2 = 10 secs(as 5 is the interval)
    

    According to your code's output:

    START===================================
    1507965166992                           <---------+
    109998                                            |
    1507965173888                                     |
    END===================================            |
    START===================================          |
    1507965176993                           <---------+
    109998                                            |
    1507965183890                                     |
    END===================================            |
    START===================================          |
    1507965186989                           <---------+
    

    Check the time difference of the marked arrow above. It executes the code exactly after 10secs(which is same as the explanation I have given above).

    Tip: Always look at and read the documentation before asking.