Async Logic - meldavy/ipf-documentation GitHub Wiki

Running asynchronous time based logic

There are a couple known approaches that are used to implement time based logic, each with specific advantages

timer

A control that is used to trigger a task on a set interval.

Code example

function SETUP_TIMER(frame)
    local timer = frame:CreateOrGetControl("timer", "addontimer", 10, 10);
    AUTO_CAST(timer);
    timer:Stop();
    timer:SetUpdateScript("AUTOAWAKENING_ON_AUTO_ATTEMPT_TIMER_TICK");
    timer:Start(1);
end

function MY_FUNCTION(frame)
    print("HI");
end

Advantages

  • Very straightforward to setup and use. Call the Stop() method of the timer to stop it.

Disadvantages

  • It is a control object, meaning that we have to go through the regular boilerplate of setting up a ui control.
  • Timer is paused from ticking when parent frame visibility becomes false (although there is a workaround)

Note

  • The first invocation always happen immediately after Start()

FPS_UPDATE

A system message that is broadcasted almost on every frame. Thus if you need a process that needs to constantly check for a certain condition, and if you need a very fine grain frequency, you can register to the FPS_UPDATE message. Within the game client, this message is used to update FPS text. But many addons use it as a good-for-almost-everything hook.

Code Example

function MYADDON_ON_INIT(addon, frame)
    addon:RegisterMsg('FPS_UPDATE', 'ON_FPS_UPDATE')
end

function ON_FPS_UPDATE(frame, msg, argStr, argNum)
    ... do stuff
end

Advantages

  • Useful if you need to constantly update some permanent UI element

Disadvantages

  • Hook always runs even if frame is not visible. Thus it can be a resource hog if not used properly. I would recommend using timer if possible.

ReserveScript(funcName, delay)

Used to invoke a function on a delay in seconds, can be as low as 0.01 second. The function is invoked only once.

Code Example

function MY_FUNC()
    ReserveScript("MY_OTHER_FUNCTION()", 2);
end

function MY_OTHER_FUNCTION()
    print("I'm invoked after a 2 second delay");
end

Advantages

  • Since we do not have mechanisms to sleep or wait without completely freezing the game, if we have some script that we want to run after an n amount of seconds, ReserveScript is the best mechanisms.
  • Unlike timer which always initially have an invocation immediately on calling timer:Start(), ReserveScript() always invokes the script after the provided delay.
  • Useful when trying to prevent race conditions by delaying an operation 0.01 seconds later

Disadvantages

  • When you want to chain multiple ReserveScript through recursion, improper recursion may cause stack overflows.
  • Each ReserveScript creates a new background thread (or at least that's what it feels like). Thus, an uncontrolled usage of ReserveScript can spawn unwanted amount of threads, and crash the game.
  • Let's say you want to call ReserveScript when a user clicks on a button, or presses a certain key. You might want to use a higher scope boolean "isWaiting" variable accompanied with a conditional statement so that you don't invoke reserve a million scripts if the user decides to mash on the button.

frame:RunUpdateScript(funcName, delay)

Similar to a timer, but without the timer control.

Code Example

func START_UPDATE(frame)
    frame:RunUpdateScript("ON_TICK", 1);
end

function ON_TICK(frame)
    if (condition) then
        return 1;
    end
    return 0;
end

RunUpdateScript works almost exactly like how a timer works. However, the tick only continues as long as the UpdateScript continues to return 1. If anything other than 1 is returned (or if nothing is returned), the UpdateScript stops running. You can also manually stop the script through StopUpdateScript(funcName)

Advantages

  • No need to create a control
  • Because the timer keeps ticking as long as 1 is returned, if you know the condition of when you want to continue running or stopping the timer, RunUpdateScript() can be much easier to use than the timer control.
  • Same other advantages as timer

Disadvantages

  • Same disadvantages as timer
  • The UpdateScript is paused from ticking when parent frame visibility becomes false (you can use same workaround as that of timer control)