A Better setTimeout


If you’ve ever used JavaScript’s setTimeout and setInterval methods. you’ve probably found them just a bit … inadequate.

  • Both return an integer rather than an actual handle.
  • If you’re using both setTimeout and setInterval, you have to remember whether the integer value is a timeout or an interval in order to call the appropriate clearXXX method.


In an ideal world, setTimeout and setInterval would return actual handle objects with a clear() method and we wouldn’t have to use clearTimeout or clearInterval. The lack of a handle really came back to bite us when we tried to use timeouts with Dojo. Dojo’s object life cycle management has an elegant this.own() which is used to register object and event handlers so that they can be cleaned up when the owning object is destroyed. The problem was that the “handle” returned by setTimeout was only an integer, not an actual handle object. We considered trying to wrap the integer, but that would just require extra code to extract the integer before passing to the clearTimeout, resulting in no real net improvement.

However, my colleague Ray Wadkins1 observed that clearTimeout and clearInterval will take a JavaScript Number object, not just an integer.

var handle = setTmeout(function(){console.log("DING DONG")}, 100);
handle = new Number(handle);
clearTimeout(handle);

This single observation opens up a whole realm of possibilities to improve setTimeout in a backward compatible way. The following adds a clear() method:

function setTimeout2(callback, delay){
  var handle = new Number(setTimeout(callback, delay));
  handle.clear = function(){clearTimeout(this)}
  return handle;
}

var handle = setTmeout2(function(){console.log("DING DONG")}, 100);
handle.clear();

The addition of clear() makes direct calls to clearTimeout unnecessary. We’ll tweak the above implementation a little to eliminate the dependence on this because we Prefer Stub Scope to this Scope. As a result, the clear() method becomes detachable.

function setTimeout3(callback, delay){
  var handle = new Number(setTimeout(callback, delay));
  handle.clear = function(){clearTimeout(handle)}
  return handle;
}

var handle = setTimeout3(function(){console.log("DING DONG")}, 200);
// We can call the clear method by a setTimeout without
// using .bind() or wrapping the call in a function()
setTimeout3(handle.clear, 0)

A Better setTimeout and setInterval

In conclusion, here are the complete versions of setTimeout and setInterval.

function setTimeout3(callback, delay){
  var handle = new Number(setTimeout(callback, delay));
  handle.clear = function(){clearTimeout(handle)}
  return handle;
}

function setInterval3(callback, delay){
  var handle = new Number(setInterval(callback, delay));
  handle.clear = function(){clearInterval(handle)}
  return handle;
}

A Better AMD setTimeout and setInterval

and packaged as AMD modules ….

define("setTimeout", [], function() {
    return function(callback, delay){
      var handle = new Number(setTimeout(callback, delay));
      handle.clear = function(){clearTimeout(handle)}
      return handle;
    }
});

define("setInterval", [], function() {
    return function(callback, delay){
      var handle = new Number(setInterval(callback, delay));
      handle.clear = function(){clearInterval(handle)}
      return handle;
    }
});

===================
UPDATE Jan 9, 2014

Ray pointed out that while the versions above work fine, both JSLint and JSHint will complain about using Number() as a constructor. As I see it, the major reason for this linter recommendation is that using Number() messes up === behavior. This does not seem to be a valid reasons in this case because I can’t envision a case where I would want the comparison of number to a setTmeout handle or the comparison of two handles to return true if they were not the same object reference. Nonetheless, it’s pointless to argue with a linter.

So while I still prefer the above compact versions, here are a few new versions incorporating Ray’s idea of using valueOf to work around the linter.

A Better Linter-safe IIFE setTimeout and setInterval

window.setTimeout = (function(setTimeout){
	return function(callback, delay){
		var toH = setTimeout(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearTimeout(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
        return handle;
	};
})(setTimeout);

window.setInterval = (function(setInterval){
	return function(callback, delay){
		var toH = setInterval(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearInterval(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
        return handle;
	};
})(setInterval);

A Better Linter-safe AMD setTimeout and setInterval

define("setTimeout", [], function(){
	var setTimeout = window.setTimeout;
	return function(callback, delay){
		var toH = setTimeout(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearTimeout(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
        return handle;
	};
});

define("setInterval", [], function(){
	var setInterval = window.setInterval;
	return function(callback, delay){
		var toH = setInterval(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearInterval(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
        return handle;
	};
});

A Better Linter-safe Dojo setTimeout and setInterval

Going back to Dojo (which uses AMD), my original motivation was for a setTimeout that could be passed to this.own(), which works with any object that has a remove() or destroy(). Thus, an additional alias to the clear() method is needed.

define("setTimeout", [], function(){
	var setTimeout = window.setTimeout;
	return function(callback, delay){
		var toH = setTimeout(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearTimeout(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
		handle.remove = handle.clear;
		return handle;
	};
});

define("setInterval", [], function(){
	var setInterval = window.setInterval;
	return function(callback, delay){
		var toH = setInterval(callback, delay);
		var handle = {
			clear: function(){
				if (toH) clearInterval(toH);
				toH = null;
			},
			valueOf: function(){ return toH;}
		};
		handle.remove = handle.clear;
		return handle;
	};
});

===================
1who needs to blog more often.

One thought on “A Better setTimeout

  1. It should be noted, since there’s a reference to Dojo’s _WidgetBase’s own() method, your AMD setTimeout and setInterval put you on the road to being able to use it with own, execept that own looks for a destroy, destroyRecursive or remove method. So, to take the AMD implementation one step further, the clear method should be aliased to a remove method as well.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s