Script execution management: setTimeoutOnce.js

In the tradition of ecmaDebug.js and <xul:serverpost/>, here’s another little idea I had and coded up.
Sometimes a function needs to be run once (as a cleanup operation, perhaps), but other functions call it more than once. In Abacus, I have at least one function (two, I think) that are run more often than they need to be. They should run, yes, but they can run after all other currently-running processes are done.
setTimeout is great, but what happens when two functions call for the same setTimeout?
I came up with setTimeoutOnce. Basically, if you have function A() call B() and C(), and both of these have the line:
setTimeoutOnce(“D()”, 0);
then after A() runs all the way through, D() executes once. Not twice, as a setTimeout would force.
It’s a very tiny script, but I’ve tested it and it works nicely. It should allow me to save some processor time in Abacus. You might also
find it useful in your own scripts.
Feedback, including bugs in the implementation, is welcome. (Remember, per the rules of my current GMail contest, useful comments here earn invites if no one else has a higher-priority claim…)

5 thoughts on “Script execution management: setTimeoutOnce.js”

  1. Nice. Very nice script thank you!
    I can’t think of a need for it now in my extensions, but very possibly I will be needing it soon.
    Thank you so much in advance 🙂

  2. Nice.
    It may be useful to have an extra optional argument that can be used to toggle whether newer calls will delay the older ons…
    What I mean is, if B() sets the timeout to 5 sec, and 1 second later C() sets it also to 5 sec, then it will be invoked appx. 5 sec after B() (4 after C()). It may be useful to delay that invocation by one second.
    After all, setTimeout basically tries to execute no earlier than the given timeout anyway 🙂

  3. It looks like it would only work when the first argument is a string? I almost always pass functions to setTimeout, as in:
    setTimeout(D, 0);
    rather than setTimout(“D()”, 0);
    You could use:
    setTimeout(setTimeoutOnce.process, aDelay, aIndex)
    in the implementation.
    (Answer: Good point! I hadn’t thought of that. Want a GMail account?)

  4. I think there are two subtle behaviours in your script, which I refrain from calling outright “bugs”, but which happen to confuse me. 🙂
    You said that your goal was to prevent the calling of a particular function more than once. However, you can do exactly that if the timeout has already been executed. Test code:
    ————————————————————-
    function A() {
    B();
    setTimeout(“C()”, 2000); //
    (From Alex: Good point, but bear in mind the intent was to prevent code from running more often than it absolutely had to. The D() function, I am assuming for this example, is a cleanup function and doesn’t have to run right this instant every time.)

  5. SCNR, but the issue your script adresses got me into tinkering mode, so I want to share my implementatin:
    ————————————————-
    function Timeout() {};
    Timeout._call = {};
    Timeout.add = function(func, delay, identical) {
    var key = identical ? func + delay : func;
    if (!Timeout._call[key]) {
    Timeout._call[key] = setTimeout(func, delay);
    }
    }
    —————————————-
    Basically I use the string representation of a function as a key for a lookup table contained in Timeout._call. Should the key be found, no timeout gets set. I’m not sure if I’m accidentally producing any side effects should the key get very long (i.e. the function has much code).
    Should it be desired that different delay times start separate timeouts, you can pass “true” as a third parameter.
    This implementation works equally well with timeout parameters passed as function or string. It can’t compare though if C == ‘C()’, obviously.

Comments are closed.