Running LUA-Code asynchroneously

From ESOUI Wiki

Jump to: navigation, search

Contents

Why would I need that?

Avoiding performance impact

Even if you stick to the "return early, return often"-doctrine, running anything in a loop can get the client to stutter, freeze, or even crash. By running things asynchronously, you can reduce the impact.

Once, I managed to identify an endless loop because it happened within LibAsync and sent my client into enough of a terrible stutter - but would let me read the debug messages, at least.

Avoiding flood kick

If you call too many expensive API functions within a certain amount of frames, you will be kicked from the server. I had this happening to me in [DailyAutoShare https://github.com/manavortex/DailyAutoShare/blob/master/DasChatMessage.lua#L59] - on invite and quest share.

How?

votan's LibAsync

LibAsync is a small utility library that was coded by the awesome @votan73. You can grab the latest release from [PotionMaker http://www.esoui.com/downloads/info405-PotionMakerforAlchemyCrafting.html].

All examples below will assume that LibAsync has been initialised like this:

local async = LibStub("LibAsync")
local task = async:Create("yourUniqueIdentifier")

You can call most functions either via async or via task. The advantage of using task is this:

task:Suspend()
task:Cancel()

Functions

local function callMe() ... end
local function callMeToo() ... end
 
task:Call(callMe):Then(callMeToo)
 
-- or defining the functions inline: 
task:Call(function() ... end):Then(function() ... end)

Note: Since defining the functions inline means that they will be defined every single time the code is run, for the sake of performance it's more viable to define them outside the function scope if you run them multiple times.

Loops

local startValue = 1
local endValue = 9000
local function someFunction(index)
  -- Do a single step
end
task:For(startValue, endValue):Do(someFunction)
local function someFunction(key, value)
  -- Do a single step
end
task:For(pairs(anyTable)):Do(someFunction)
local function someFunction(index, value)
  -- Do a single step
end
task:For(ipairs(anyTable)):Do(someFunction)

zo_callLater

zo_callLater is ZOS's fire-and-forget way of "triggering" events. Overusing it can lead to significant performance drops.

The function takes two arguments, the first is the function you want to call, and the second is the delay in milliseconds.

local function callMeToo() 
  d("I was called five seconds later: " ..  GetTimeString())
end
 
local function callMe() 
  d("I was called at " ..  GetTimeString())
  zo_callLater(callMeToo, 5000)
end
callMe()

passing function arguments:

In order to pass arguments to zo_callLater one can either register an anonymous function as the function argument of zo_callLater:

    ...
    zo_callLater(function() callMeToo(arg1, arg2) end, 5000)

or use the fact that zo_callLater will return an id that will be passed to the function call:

local paramTable = {}
 
local function myfunc(id)
 
    local params = paramTable[id]
    ...
 
end
 
local id = zo_callLater(myfunc, ms)
paramTable[id] = {...}

RegisterForUpdate

You can register a function for update with the event manager in the following way:

local identifier = "myReallyUniqueIdentifier4711"
local function callMe() d("I will be repeatedly called every 5 seconds until I am unregistered again") end
EVENT_MANAGER:RegisterForUpdate(identifier, callMe, 5000)
EVENT_MANAGER:UnregisterForUpdate(identifier)

... which is basically the same as zo_callLater.


ArtOfShred:
There is one nice advantage to RegisterForUpdate, and that is you can make a continuous throttle by reregistering the update function whenever a certain function triggers.
I use this in LUI Extended to make a "x" variable duration throttle timer for Experience Gain for example. If the player goes 5 seconds without gaining XP then the saved value is dumped, reset, and the event is unregistered. If the player gains XP again during that 5 sec window, the event is reregistered and thus the timer restarts.

local ZO_CallLaterId = 1
function zo_callLater(func, ms)
    local id = ZO_CallLaterId
    local name = "CallLaterFunction"..id
    ZO_CallLaterId = ZO_CallLaterId + 1
 
    EVENT_MANAGER:RegisterForUpdate(name, ms,
        function()
            EVENT_MANAGER:UnregisterForUpdate(name)
            func(id)
        end)
    return id
end

This wiki page was created after this forum thread

Personal tools
Namespaces
Variants
Actions
Menu
Wiki
Toolbox