Markus
Markus

Reputation: 1557

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....

Upvotes: 26

Views: 59798

Answers (9)

splattne
splattne

Reputation: 104070

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"]); 

Upvotes: 5

Bruce
Bruce

Reputation: 1

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!

Upvotes: 0

hasen
hasen

Reputation: 166352

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 ..

Upvotes: 31

RudiBR
RudiBR

Reputation: 117

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 )
}

Upvotes: 1

Kent Fredric
Kent Fredric

Reputation: 57384

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.

Upvotes: 2

Rick Strahl
Rick Strahl

Reputation: 17671

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.

Upvotes: 0

Jared
Jared

Reputation: 8610

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>

Upvotes: 0

M&#225;r &#214;rlygsson
M&#225;r &#214;rlygsson

Reputation: 14606

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);
  }
}

Upvotes: 1

Daniel Schaffer
Daniel Schaffer

Reputation: 57872

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);

Upvotes: 2

Related Questions