Ambiguous Timeout operation

Posted on
  • @Gordon, Tried this with Espruino board 1v4 and Pico running both 1v89 and 1v84 builds, as well as nightly build from @DrAzzy...

    Executing the following lines from the clearTimeout reference together (i.e. cut and paste both lines into IDE left pane) works as expected...

    var id = setTimeout(function () { print('foo'); }, 1000); // returns 1 and never prints 'foo'
    clearTimeout(id);
    

    But if you enter the first line, wait for the timeout to "fire" (which prints foo), then enter the second line, it results in...

    Uncaught Error: Unknown Timeout
    at line 1 col 16
    clearTimeout(id);

    Obviously, when the Timeout fires it internally clears the Timeout ID, but the code leaves id holding what seems to be a "valid Timeout ID". The code outside the timeout has no way of knowing whether or not the timeout has fired, which prevents arbitrarily cancelling a timeout that has potentially occurred. NodeJS does not exhibit this ambiguous behavior. My workaround has been to use a try/catch clause around the clearTimeout as per the example below...

    var id = setTimeout(function () { console.log('foo'); }, 1000);
    setTimeout(function(){ try {clearTimeout(id);} catch(e) {console.log('error:',e);} },2000);
    
  • @CanyonCasa, indeed, there is a difference between Espruino and most JavaScript VMs: If you do a clearTimeout() or clearInterval() which have already fired or been cleared, Espruino is not happy where the others just ignore...

    A good practice for handling setTimeout()s is this pattern:

    • clear the handle in the timed function
    • check the handle before clearing the timeout and clear the handle

      var timeoutId;
      // some code...
      timeoutId = setTimeout(function(){
       timeoutId = null;
       // ...your other code....
      }, 10000);
      // ...other code...
      // code to clear the timeout ALLWAYS conditioned
      if (timeoutId) { timeoutId = clearTimeout(timeoutId); }
      

    For setInterval(), the pattern is a bit simpler:

    • check the handle before clearing the interval and clear the handle

    In both cases, the handle is a nice 'handle' to know whether a timeout is still going on or an interval is already or still going on.

    The pattern for handling timeouts become a bit tricky when the same function is used in multiple, concurrent timeouts. Even though it is easy to pass parameters in the setTimeout() and the timed function, the parameter is not ready until timeout construction function returns... Using the 'trick' to pass an object and have it updated afterwards overcomes this hurdle:

    var timeout1 = {}, timeout2 = {};
    function timedFunction(timeout) {
      timeout.id = null;
      // ...other code...
    };
    // ...more code...
    timeout1.id = setTimeout(timedFunction, 1000, timeout1);
    timeout2.id = setTimeout(timedFunction, 2000, timeout2);
    // ...some code...
    if (timeout1.id) { timeout1.id = clearTimeout(timeout1.id); }
    // ...some code again...
    if (timeout2.id) { timeout1.id = clearTimeout(timeout2.id); }
    

    Thanks for making me think through this concurrent reuse situation... so far never happened to me and the simple version worked always as desired... and does no harm when cross developing in HTML5 in Browser.

    As an allObjects's goodie, you can go all-things-are-objects-crazy and use a full fletched object to handle it nicely with class-y, object-oriented timeout instances. You can give the timeouts even names and some debug support about when set, fired, and cleared... (those fancy enhancements we leave for now with the 'dream(s)'... of which you should never ever make all come true,... running out of dreams: what would life be without dreams...)

    // setup the Timeout 'class' with 'clear()' method clearing conditionally
    function Timeout() { this.id = null; }
    Timeout.prototype.clear = function() {
      if (this.id) { this.id = clearTimeout(this.id); } };
    // some code...
    // setup timeout control object
    var timeout1 = new Timeout(), timeout2 = new Timeout();
    // some code...
    // setup timeouts
    timeout1.id = setTimeout(timedFunction,1000,timeout1);
    timeout2.id = setTimeout(timedFunction,2000,timeout2);
    // some code...
    timeout1.clear(); // conditioned only when other code needs to be included
    timeout2.clear(); // conditioned only when other code needs to be included
    

    The extra solution would be luxury Timeout class that wraps the simple language setTimeout() function... but 'boiling the ocean' could backfire: memory waste / 'leaks' (application owned) in memory-frugal MC world...

  • Thanks @allObjects. Your first example will work for my application, cleaner than my workaround. I've seen this signature before, but haven't needed it for NodeJS, where I do most of my coding these days.
    Still, I don't see the value in throwing an error from clearTimeout where undefined could be gracefully returned instead, which doesn't require the user to know to clear the ID in the callback, or think of clearTimeout differently than clearInterval or differently than NodeJs code.
    FYI, I think your second example has a couple typos ...

    var timeout1 = {}, timeout2 = {};
    function timedFunction(timeout) {
      timeout.id = null;
      // ...other code...
    };
    // ...more code...
    timeout1.id = setTimeout(timedFunction, 1000, timeout1);
    timeout2.id = setTimeout(timedFunction, 2000, timeout2);
    // ...some code...
    if (timeout1.id) { timeout1.id = clearTimeout(timeout1.id); }
    // ...some code again...
    if (timeout2.id) { timeout2.id = clearTimeout(timeout2.id); }
    

    And similarly, I believe you meant line 11 of the last example to reference timeout2.

  • @CanyonCasa, thanks being so gentle with couple typos... it was more heaps of... anyway...

    I had't the opportunity to do much node.js... I'm still heavily involved in all other server technologies, even though JavaScript is my primary choice of language / environment - because it reminds me the most of Smalltalk where everything is an object, hence my forum ID. The last Smalltalk VM's I worked with had JITs that took care of compiling it to machine code beyond byte code and took at the same time care of the primitives (which from my language point of view was one of the very bad things Java introduced from the beginning and only lately added auto conversion for). Client-side I did a lot with emerging JavaScript frameworks and did also demanding Single Page Apps. - I just loved st and am still grateful that it kicked my brain b..t to grok oo and bringing relieve to finally do oo without having just to emulate it (with structured language). Another great thing I liked the very helpful method signature pattern that finally got away with the noise of introduced by the gazillions of parenthesis and commas that C - and practically every other language - still hangs on to... not to talk about the cluttering getter and setter verbosity.

    I'm looking forward to hear more from your experience in the forum to pair up the Espruino and node.js worlds, especially when it comes to servers participate in the 'game'.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Ambiguous Timeout operation

Posted by Avatar for CanyonCasa @CanyonCasa

Actions