Understand timer.performWithDelay() Better

** BEGIN DISCLAIMER **
The following is all based on my experience using timer.* 
AFAIK, this is not officially documented, so behaviors could in theory change.
** END DISCLAIMER **
 
A member of our community asked an interesting question in the ‘Slack’ #random channel and I answered.
 
Then I thought, hmm… this would make a good random forums post too:

Timer events behave as follows:

Order Of Execution (same times)

timer.performWithDelay( 100, funcA ) timer.performWithDelay( 100, funcB )
  • funcA will always be executed before funcB
    Order Of Execution (different times) Example #1

    timer.performWithDelay( 100, funcA ) timer.performWithDelay( 10, funcB )

  • funcB will always be executed before funcA
    Order Of Execution (different times) Example #2

    timer.performWithDelay( 5, funcA ) timer.performWithDelay( 10, funcB )

  • funcA will always be executed before funcB

  • Neither function will be executed any sooner than the next frame, AND  funcB will happen immediately after funcA (there will be no 5ms separation).

  • Because both timers are less than a frame duration, they are fully elapsed by the time the next frame starts, thus they trigger right away.
    Minimum Durations Example #1

    timer.performWithDelay( 1, funcA ) timer.performWithDelay( 1, funcB )

  • funcA will always be executed before funcB

  • Neither function will be executed any sooner than the next frame.
    Minimum Durations  Example #2

    timer.performWithDelay( 0, funcA )

  • funcA will be executed this frame (last time I checked this was true)

The nuances of this explanation are incredibly helpful.  I hadn’t really put together the relationship between frame rate and delays or understood that two delayed timers might execute without the specified delay (depending on their relationship to the frame rate).  Great topic!

@Corona, can we pin this one to the top of the forum?

just keep in mind that execution order is undocumented/undefined, so could change on a whim.  (which is why i personally avoid timer - it’s easily replaced as a DIY where you’ll have known results)

so while it’s a worthwhile effort to report “experimental” results, it’s no substitute for proper documentation, as official docs also serve to establish an implied commitment to maintain the same non-breaking behavior in future builds.

for example, the behavior of multiple iterations when interval is sub-frame.  (fe:  delay=5ms, iterations=-1)  any casual use will quickly reveal that you only get at max 1 callback per frame no matter what, then the next interval is reset based off current time plus interval, thus maintaining the one callback per frame…

…BUT, you could just as easily imagine a design where that scenario would generate _ 6 _ callbacks per frame at 30fps (or even an occasional 7th, to account for accumulated rounding).  the difference:  “next_callback_time” would be calc’d as “this_callback_time + interval” instead of “current_frame_time + interval”.  sure, they’d all “quantize” to the next frame time, still not truly 5ms real-time between each callback, but the “sum” of interval * #callbacks would “add up” in a more-sensible way to account for total real-time elapsed.

i’m not suggesting it should work that way!  but it would have been a valid way to design it.  you’d just want the behavior documented… as should be the current behavior.

@davebollinger,

Absolutely. I agree it could change, but I think this is a fundamental architectural element which is pretty well entrenched for now. 

If Corona ever did a re-write, re-architect, or for example decided to go multi-threaded, then this behavior very well could change.

That said, if the behavior were to change there would probably be an uproar because many folks (not just myself) have become accustomed to the current behavior and purposely (or accidentally) rely on it.

I think the best outcome of the points you’ve raised is for us (community regulars) to push for this to go into the official docs, therefore cementing it further as a reliable and expect-able behavior.

@davebollinger  Do you use transitions to define and control your delays?

@roaminggamer  I have had some bizarro AI results and your explanations about have revealed some of the problematic assumptions I was making.

What do the two of you think of this concept?:  Making my initial AI loop for each object with timerPerformWithDelay (set to infinite repetitions) and then micromanaging the execution of time-behaviors with transition delays for more exact and predictable results (for AI-triggered object behaviors)? 

i don’t use transitions (or timers), just a single enterFrame - since everything can be “reimagined” as a frame event.

would you know how to break up a for loop for discrete frame-based processing?  (ie, increment per frame)

if so, then you can add a “timer” to your frame handler’s to-do list (count time/fps, callback when done)

if so, then you can add a “transition” to your frame handler’s to-do list (count 0…1, callback during)

and, of course, the infinite loop (aka c/java’s “for (;;)” or “while (true)”, ie do no counting), callback during, is a “normal” frame event

i tend to put only “high-level” callbacks here, then those dispatch further at lower levels as needed.  fe just one frame handler for the game class (who then dispatches updates to whatever collection of entities it owns that need it)

wanna pause/resume everything?  simply set a state in your frame handler to stop processing its to-do list

wanna run your game loop at half speed?  perform its callback every other frame

want certain “timers” to occur before your game loop, others after the game loop?  can do

want a count of active “timers” or “transitions” or “enterFrames”?  can do

etc

eventually you might put your own pub/sub system here too, and be done with Runtime entirely. (except for its ONE enterFrame)

ever wish you could add event handlers to non-display-objects?  (ie, not children of EventDispatcher, fe a plain table)  can do

etc

it’s great that corona provides stock versions of all that functionality.  but every one of them is “too opaque” for my liking, so i reinvent wheels.

btw/aside:  i don’t recommend my approach to others - you either discover the need for it on your own, and do it yourself, or you don’t, and that’s fine - i have no interest in “preaching” this approach, but you asked, so… :smiley:

Paul Robson published his framework, fe:  https://github.com/autismuk/Executive/blob/master/system/executive.lua

his is far more elaborate than mine (in most respects) but same concept – everything derives from a single frame-based loop.

you’ll find rewritten versions of timers, transitions, pub/sub, etc.  (again, not exactly how i do it, but == conceptually)

@davebollinger Thanks!  I have everything running on a single enterframe loop - except AI which is object specific at this time (each one on an individual timer).  I tightly track all of the timers, transitions and objects but I do get some unexpected results with my AI timer loops.  I’ve just let them slide as sometimes a little AI randomness makes the system look smarter somehow.  Like if the timer is off and the creature moves forward instead of backward, it can just look like it decided to be aggressive instead of running away like it should have and then the player ascribes emotion to the object - “it must be angry!” - which makes the AI seem more sophisticated than it is.

Still, I like to control everything under the hood (or at least understand it - like I do now for timers) and add my own randomness  :wink:

I remember seeing this article which might be applicable http://web.archive.org/web/20160320093509/http://www.gymbyl.com:80/read/accurate-timing-in-corona.html

The nuances of this explanation are incredibly helpful.  I hadn’t really put together the relationship between frame rate and delays or understood that two delayed timers might execute without the specified delay (depending on their relationship to the frame rate).  Great topic!

@Corona, can we pin this one to the top of the forum?

just keep in mind that execution order is undocumented/undefined, so could change on a whim.  (which is why i personally avoid timer - it’s easily replaced as a DIY where you’ll have known results)

so while it’s a worthwhile effort to report “experimental” results, it’s no substitute for proper documentation, as official docs also serve to establish an implied commitment to maintain the same non-breaking behavior in future builds.

for example, the behavior of multiple iterations when interval is sub-frame.  (fe:  delay=5ms, iterations=-1)  any casual use will quickly reveal that you only get at max 1 callback per frame no matter what, then the next interval is reset based off current time plus interval, thus maintaining the one callback per frame…

…BUT, you could just as easily imagine a design where that scenario would generate _ 6 _ callbacks per frame at 30fps (or even an occasional 7th, to account for accumulated rounding).  the difference:  “next_callback_time” would be calc’d as “this_callback_time + interval” instead of “current_frame_time + interval”.  sure, they’d all “quantize” to the next frame time, still not truly 5ms real-time between each callback, but the “sum” of interval * #callbacks would “add up” in a more-sensible way to account for total real-time elapsed.

i’m not suggesting it should work that way!  but it would have been a valid way to design it.  you’d just want the behavior documented… as should be the current behavior.

@davebollinger,

Absolutely. I agree it could change, but I think this is a fundamental architectural element which is pretty well entrenched for now. 

If Corona ever did a re-write, re-architect, or for example decided to go multi-threaded, then this behavior very well could change.

That said, if the behavior were to change there would probably be an uproar because many folks (not just myself) have become accustomed to the current behavior and purposely (or accidentally) rely on it.

I think the best outcome of the points you’ve raised is for us (community regulars) to push for this to go into the official docs, therefore cementing it further as a reliable and expect-able behavior.

@davebollinger  Do you use transitions to define and control your delays?

@roaminggamer  I have had some bizarro AI results and your explanations about have revealed some of the problematic assumptions I was making.

What do the two of you think of this concept?:  Making my initial AI loop for each object with timerPerformWithDelay (set to infinite repetitions) and then micromanaging the execution of time-behaviors with transition delays for more exact and predictable results (for AI-triggered object behaviors)? 

i don’t use transitions (or timers), just a single enterFrame - since everything can be “reimagined” as a frame event.

would you know how to break up a for loop for discrete frame-based processing?  (ie, increment per frame)

if so, then you can add a “timer” to your frame handler’s to-do list (count time/fps, callback when done)

if so, then you can add a “transition” to your frame handler’s to-do list (count 0…1, callback during)

and, of course, the infinite loop (aka c/java’s “for (;;)” or “while (true)”, ie do no counting), callback during, is a “normal” frame event

i tend to put only “high-level” callbacks here, then those dispatch further at lower levels as needed.  fe just one frame handler for the game class (who then dispatches updates to whatever collection of entities it owns that need it)

wanna pause/resume everything?  simply set a state in your frame handler to stop processing its to-do list

wanna run your game loop at half speed?  perform its callback every other frame

want certain “timers” to occur before your game loop, others after the game loop?  can do

want a count of active “timers” or “transitions” or “enterFrames”?  can do

etc

eventually you might put your own pub/sub system here too, and be done with Runtime entirely. (except for its ONE enterFrame)

ever wish you could add event handlers to non-display-objects?  (ie, not children of EventDispatcher, fe a plain table)  can do

etc

it’s great that corona provides stock versions of all that functionality.  but every one of them is “too opaque” for my liking, so i reinvent wheels.

btw/aside:  i don’t recommend my approach to others - you either discover the need for it on your own, and do it yourself, or you don’t, and that’s fine - i have no interest in “preaching” this approach, but you asked, so… :smiley:

Paul Robson published his framework, fe:  https://github.com/autismuk/Executive/blob/master/system/executive.lua

his is far more elaborate than mine (in most respects) but same concept – everything derives from a single frame-based loop.

you’ll find rewritten versions of timers, transitions, pub/sub, etc.  (again, not exactly how i do it, but == conceptually)

@davebollinger Thanks!  I have everything running on a single enterframe loop - except AI which is object specific at this time (each one on an individual timer).  I tightly track all of the timers, transitions and objects but I do get some unexpected results with my AI timer loops.  I’ve just let them slide as sometimes a little AI randomness makes the system look smarter somehow.  Like if the timer is off and the creature moves forward instead of backward, it can just look like it decided to be aggressive instead of running away like it should have and then the player ascribes emotion to the object - “it must be angry!” - which makes the AI seem more sophisticated than it is.

Still, I like to control everything under the hood (or at least understand it - like I do now for timers) and add my own randomness  :wink:

I remember seeing this article which might be applicable http://web.archive.org/web/20160320093509/http://www.gymbyl.com:80/read/accurate-timing-in-corona.html