tags:

views:

2293

answers:

7

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

A: 
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);
Daniel Schaffer
This answer doesn't address the problem stated in the question.
Már Örlygsson
A: 

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);
  }
}
Már Örlygsson
A: 

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>
Jared
+6  A: 

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

hasen j
Uhm, actually, your code does not really suppress multiple timeout "threads". (Note how the timeout in the question is recursive.) More is needed. ;)
Már Örlygsson
A: 

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.

Rick Strahl
+1  A: 

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

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.

Kent Fredric