ESOUI

ESOUI (https://www.esoui.com/forums/index.php)
-   Lua/XML Help (https://www.esoui.com/forums/forumdisplay.php?f=175)
-   -   Wait for a function to end before continue (https://www.esoui.com/forums/showthread.php?t=2213)

Klingo 09/07/14 02:51 AM

Wait for a function to end before continue
 
Hi

I have a function that is triggered by an event and within this function there are several sub-functions that are called.
The first sub-functions makes use of some "zo_callLater" and the end of this sub-function is reached earlier than all the "zo_callLater" are ended (they might take 2-3 seconds). So the second sub-function is already called although not all "zo_callLater" from the first sub-function have ended, which causes some problems in the addon.

Currently I deal with this situation in the following way:
The first sub-function returns "true" as soon as all "zo_callLater" calls are completed. I then check this value in a loop if it is "true" and only in that case allow the main function to call the second sub-function.
Lua Code:
  1. local mainFunction()
  2.     -- call the first sub-function that contains some 'zo_callLater'
  3.     local isCompleted = callFirstSubFunction()
  4.  
  5.     while (isCompleted == nil) do
  6.         -- do nothing; wait
  7.     end
  8.  
  9.     -- call the second sub-function
  10.     callAnotherSubFunction()
  11. end

It basically works, but I am not sure how bad (performance-wise) this solution is.
I don't see any game-freezing effects like a regular endless-loop would cause, but I thought there might be a better solution for this?

Klingo

Wykkyd 09/07/14 05:10 AM

This is when I typically use the update tic. Update tic callbacks are "asynchronous". Your wait loop is "synchronous". It technically causes more churn, and locks the process until that loop ends.

Code:

local firstRunCalled = false
local firstRunComplete = false
local callFirst = function(...)
    -- do whatever has to happen first
    firstRunComplete = true
end
local callSecond = function()
    -- do your second thing here
end
local mainFunction = function(eventType,...)
    if firstRunCalled and eventType ~= nil then return end
        -- eventType having a value means it came from the event handler, not our update tic.
        -- Since we're already running, skip it until last pass completes
    if not firstRunCalled and eventType == nil then return end
        -- standard update tic but we're not processing
        -- so kick us out
    if not firstRunCalled then
        firstRunCalled = true
        firstRunComplete = false
        callFirst( ... )
    end
    if not firstRunComplete then return end
        -- kick us out if it isn't complete yet
    firstRunCalled = false
    callSecond()
end
EVENT_MANAGER:RegisterForUpdate("myaddon_mainfunction_updatetic", 100, mainFunction)
EVENT_MANAGER:RegisterForEvent( "myaddon_mainfunction_eventwatch", EVENT_WHATEVER, mainFunction)

Something like that, perhaps. Sorry, this is off the top of my head. Basically hook your mainFunction into both the event, and the update callback.

merlight 09/07/14 06:28 AM

Klingo, the snippet you posted looks ripped out of context, and unlike what you described. isCompleted is assigned exactly once, never changes value, so that loop is either done immediately, or runs forever :)

I'd slightly modify Wykkyd's method. In your event handler, check if an update func is registered - if yes, return immediately (or note that the whole process needs restarting; depends on what your addon does with the event), otherwise do required preparations and register for update. In update you can check elapsed time, call other functions when they're due, and after the last one is finished, unregister (or restart if there was another event).

Sasky 09/07/14 01:33 PM

Quote:

Originally Posted by Klingo (Post 12008)
Hi

I have a function that is triggered by an event and within this function there are several sub-functions that are called.
The first sub-functions makes use of some "zo_callLater" and the end of this sub-function is reached earlier than all the "zo_callLater" are ended (they might take 2-3 seconds). So the second sub-function is already called although not all "zo_callLater" from the first sub-function have ended, which causes some problems in the addon.

Currently I deal with this situation in the following way:
The first sub-function returns "true" as soon as all "zo_callLater" calls are completed. I then check this value in a loop if it is "true" and only in that case allow the main function to call the second sub-function.
Lua Code:
  1. local mainFunction()
  2.     -- call the first sub-function that contains some 'zo_callLater'
  3.     local isCompleted = callFirstSubFunction()
  4.  
  5.     while (isCompleted == nil) do
  6.         -- do nothing; wait
  7.     end
  8.  
  9.     -- call the second sub-function
  10.     callAnotherSubFunction()
  11. end

It basically works, but I am not sure how bad (performance-wise) this solution is.
I don't see any game-freezing effects like a regular endless-loop would cause, but I thought there might be a better solution for this?

Klingo

That's called busy waiting and is probably the worst-performance for waiting on something. You eat resources checking constantly, even though you don't need to.

The solution Wykkyd provided basically tones down how often you check.

If you have access to all functions you could also do something like this, where the check is at the end of each sub-call. This would keep the times you check for updates to a minimum.

Lua Code:
  1. local foo_done = false
  2. local bar_done = false
  3.  
  4. function checkEnd()
  5.     if foo_done and bar_done then
  6.         --call end stuff
  7.     end
  8. end
  9.  
  10. function foo()
  11.     --Do stuff
  12.     foo_done = true
  13.     checkEnd()
  14. end
  15.  
  16. function bar()
  17.     --Do other stuff
  18.     bar_done = true
  19.     checkEnd()
  20. end
  21.  
  22. function mainFunction()
  23.     foo_done = false
  24.     bar_done = false
  25.     --asynchronous calls
  26.     zo_callLater(foo, 100)
  27.     zo_callLater(bar, 100)
  28. end

Seerah 09/07/14 03:08 PM

There are two even better ways to accomplish this that do not use any sort of a timer to poll to see if it's ready.

#1 - Have the script you assign to the zo_callLater call actually call the next function when it's finished.
Lua Code:
  1. local function callAnotherSubFunction()
  2.      --do other stuff after zo_callLater is finished
  3. end
  4.  
  5. local function callFirstSubFunction()
  6.      --do stuff
  7.      zo_callLater(100, function()
  8.                --do something later
  9.                callAnotherSubFunction()
  10.           end)
  11. end
  12.  
  13. local function mainFunction()
  14.     callFirstSubFunction()
  15. end


#2 - Use callbacks. Register for a custom callback (a custom event) which is fired by the zo_callLater call, and have your callback handler be the second function.
Lua Code:
  1. local function callFirstSubFunction()
  2.      --do stuff
  3.      zo_callLater(100, function()
  4.                --do something later
  5.                CALLBACK_MANAGER:FireCallback("zo_callLaterFinished")
  6.           end)
  7. end
  8.  
  9. local function callAnotherSubFunction()
  10.      --do other stuff after zo_callLater is finished
  11. end
  12.  
  13. local function mainFunction()
  14.     callFirstSubFunction()
  15. end
  16.  
  17. CALLBACK_MANAGER:RegisterCallback("zo_callLaterFinished", callAnotherSubFunction)

Fyrakin 09/11/14 02:36 PM

Quote:

Originally Posted by Seerah (Post 12020)
There are two even better ways to accomplish this that do not use any sort of a timer to poll to see if it's ready.

I'd go with callbacks for this. Its more efficient.

Seerah 09/12/14 12:38 PM

Quote:

Originally Posted by Fyrakin (Post 12067)
I'd go with callbacks for this. Its more efficient.

Oops - yes. I forgot to put that this would be my preference.

Klingo 09/14/14 07:19 AM

Thanks a lot for all the feedback, it's much appreciated!
The solution with callbacks definitely seems to be the best way to go on with.


All times are GMT -6. The time now is 07:13 PM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2014 - 2022 MMOUI