How do you handle multiple instances of setTimeout()?

What is the most recommended/best way to stop multiple instances of a setTimeout function from being created (in javascript)?

An example (psuedo code):

function mouseClick()
{
   moveDiv("div_0001", mouseX, mouseY);
}

function moveDiv(objID, destX, destY)
{
   //some code that moves the div closer to destination
   ...
   ...
   ...

   setTimeout("moveDiv(objID, destX, destY)", 1000);
   ...
   ...
   ...
}

My issue is that if the user clicks the mouse multiple times, I have multiple instances of moveDiv() getting called.

The option I have seen is to create a flag, that only allows the timeout to be called if no other instance is available...is that the best way to go?

I hope that makes it clear....

Answers:

Answer

when you call settimeout, it returns you a variable "handle" (a number, I think)

if you call settimeout a second time, you should first

clearTimeout( handle )

then:

handle = setTimeout( ... )

to help automate this, you might use a wrapper that associates timeout calls with a string (i.e. the div's id, or anything you want), so that if there's a previous settimeout with the same "string", it clears it for you automatically before setting it again,

You would use an array (i.e. dictionary/hashmap) to associate strings with handles.

var timeout_handles = []    
function set_time_out( id, code, time ) /// wrapper
{
    if( id in timeout_handles )
    {
        clearTimeout( timeout_handles[id] )
    }

    timeout_handles[id] = setTimeout( code, time )
}

There are of course other ways to do this ..

Answer

I haven't tested any of this, and just cut this up in the editor here. Might work, might not, hopefully will be food for thought though.

var Timeout = { 
   _timeouts: {}, 
   set: function(name, func, time){ 
     this.clear(name); 
     this._timeouts[name] = {pending: true, func: func}; 
     var tobj = this._timeouts[name];
     tobj.timeout = setTimeout(function()
     { 
/* setTimeout normally passes an accuracy report on some browsers, this just forwards that. */
       tobj.func.call(arguments); 
       tobj.pending = false;
     }, time); 
   },
   hasRun: function(name)
   { 
       if( this._timeouts[name] ) 
       {
          return !this._timeouts[name].pending; 
       }
       return -1; /* Whut? */ 
   },
   runNow: function(name)
   {
      if( this._timeouts[name] && this.hasRun(name)===false )
      {
         this._timeouts[name].func(-1); /* fake time. *shrug* */
         this.clear(name);
      }
   } 
   clear: function(name)
   {
     if( this._timeouts[name] && this._timeouts[name].pending ) 
     {
       clearTimeout(this._timeouts[name].timeout); 
       this._timeouts[name].pending = false; 
     }
   }
};

Timeout.set("doom1", function(){ 
  if(  Timeout.hasRun("doom2") === true )
  {
     alert("OMG, it has teh run");  
  }
}, 2000 ); 
Timeout.set("doom2", function(){ 
   /* NooP! */
}, 1000 ); 

Successive calls with the same identifier will cancel the previous call.

Answer

I would do it this way:

// declare an array for all the timeOuts
var timeOuts = new Array();  

// then instead of a normal timeOut call do this
timeOuts["uniqueId"] = setTimeout('whateverYouDo("fooValue")', 1000);  

// to clear them all, just call this
function clearTimeouts() {  
  for (key in timeOuts) {  
    clearTimeout(timeOuts[key]);  
  }  
}  

// clear just one of the timeOuts this way
clearTimeout(timeOuts["uniqueId"]); 
Answer

You could store multiple flags in a lookup-table (hash) using objID as a key.

var moving = {};

function mouseClick()
{
  var objID = "div_0001";
  if (!moving[objID])
  {
    moving[objID] = true;
    moveDiv("div_0001", mouseX, mouseY);
  }
}
Answer

You can avoid a global or lesser variable by using a property within the function. This works well if the function is only used for this specific context.

function set_time_out( id, code, time ) /// wrapper
{
  if(typeof this.timeout_handles == 'undefined') this.timeout_handles = [];

        if( id in this.timeout_handles )
        {
                clearTimeout( this.timeout_handles[id] )
        }

        this.timeout_handles[id] = setTimeout( code, time )
}
Answer
var timeout1 = window.setTimeout('doSomething();', 1000);
var timeout2 = window.setTimeout('doSomething();', 1000);
var timeout3 = window.setTimeout('doSomething();', 1000);

// to cancel:
window.clearTimeout(timeout1);
window.clearTimeout(timeout2);
window.clearTimeout(timeout3);
Answer

you can always overwrite the buttons onclick to return false. example:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="UTF-8">
<head>
    <title>Javascript example</title>
    <script type="text/javascript">         
        var count = 0;
        function annoy() {
            document.getElementById('testa').onclick = function() { return false; };

            setTimeout(function() {
                alert('isn\'t this annoying? ' + count++);
                document.getElementById('testa').onclick = window.annoy;
            }, 1000);

        }
    </script>
</head>
<body>
    <h2>Javascript example</h2>
    <a href="#" onClick="annoy()" id="testa">Should Only Fire Once</a><br />
</body>
</html>
Answer

You can set a global flag somewhere (like var mouseMoveActive = false;) that tells you whether you are already in a call and if so not start the next one. You set the flag just before you enter the setTimeout call, after checking whether it's already set. Then at the end of the routine called in setTimeout() you can reset the flag.

Answer

I'm using this to force a garbage collection on all obsolete timeout references which really un-lagged my script preformance:

var TopObjList = new Array();
function ColorCycle( theId, theIndex, RefPoint ) {
    ...
    ...
    ...
    TopObjList.push(setTimeout( function() { ColorCycle( theId, theIndex ,CCr ); },CC_speed));
    TO_l = TopObjList.length;
    if (TO_l > 8888) {
        for (CCl=4777; CCl<TO_l; CCl++) {
            clearTimeout(TopObjList.shift());
            }
        }
    }

My original sloppy code was generating a massive array 100,000+ deep over a very short time but this really did the trick!

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.