polyfill solution
Here is some javascript that I updated from a previous project, now it has been expanded using trigger and update methods; it is similar to John Wickholm's solution (+1) - but a bit more complete, given clearing, passing arguments, and preventing eval
if necessary:
(function(keep){ /// a few things to remember keep.setTimeout = window.setTimeout; keep.clearTimeout = window.clearTimeout; keep.TO = function(){}; keep.list = {}; keep.settings = { eval: false /// set this to true if you wish to support string timeouts }; /** * Quick check function to prevent eval */ keep.checkParam = function( param ){ if ( !keep.settings.eval && typeof param == 'string' ) { throw new Error('setTimeout blocked evaluation of string, ' + 'use a function instead.'); return false; } else if ( param ) { return true; } }; /** * Simple function constructor to avoid trapping unwanted references */ keep.makeFunction = function(data){ return function(args){ /// copy our args array args = data.args.slice(); /// do we allow eval? if ( keep.settings.eval ) { /// if so, reuse setTimeout for it abilities args[0] = data.param; /// use the original param args[1] = 0; /// trigger immediately keep.setTimeout.apply( window, args ); } // more secure, assume dealing with function -- not string else if ( keep.checkParam( data.param ) && data.param.apply ) { data.param.apply( window, args.slice(2) ); } else { throw new Error('unsupported param for setTimeout' + ' ie non-function used.'); } /// clear our storage of this tid window.clearTimeout( data.tid ); }; }; /** * Sets timeouts just like you would expect */ window.setTimeout = function( param, timeout ){ if ( keep.checkParam( param ) ) { var tid, data; /// support passing a timeout object as param if ( param instanceof keep.TO ) { data = param; data.args[1] = data.timeout; } else { /// create an object to store the timeout info data = new keep.TO(); data.func = keep.makeFunction(data); data.param = param; data.timeout = timeout; data.args = Array.prototype.slice.call(arguments,0); data.args[0] = data.func; } data.tid = keep.setTimeout.apply( window, data.args ); keep.list[data.tid] = data; /// enhance the returned number to support .clear, .trigger and .update tid = new Number(data.tid); tid.clear = window.clearTimeout; tid.trigger = window.triggerTimeout; tid.update = window.updateTimeout; return tid; } }; /** * Clearing timeouts since 2013 */ window.clearTimeout = function( tid ){ if ( this instanceof Number ) { tid = 0 + this; } var obj; if ( (obj = window.getTimeout(tid)) ) { delete keep.list[tid]; keep.clearTimeout.call(window, tid); } }; /** * Returns the internal timeout storage object */ window.getTimeout = function( tid ){ var obj; if ( (obj = keep.list[tid]) ) { return obj; } }; /** * Clears and fires a timeout before it outed time */ window.triggerTimeout = function( tid ){ if ( this instanceof Number ) { tid = 0 + this; } var obj; if ( (obj = window.getTimeout(tid)) ) { window.clearTimeout(tid); obj.func.call(window); } else { throw new Error('No Timeout found to trigger for ID '+ tid); } }; /** * Clears and recreates an existing timeout, returns a new timeout id. */ window.updateTimeout = function( tid, timeout ){ if ( this instanceof Number ) { if ( arguments.length == 1 ) { timeout = tid; } tid = 0 + this; } var obj; if ( (obj = window.getTimeout(tid)) ) { obj.timeout = timeout; window.clearTimeout(tid); return window.setTimeout(obj); } else { throw new Error('No Timeout found to update for ID ' + tid); } }; /** * Utility function to tidy up */ window.clearAllTimeouts = function(){ for ( var i in keep.list ) { window.clearTimeout(i); }; }; /// Tidy up window.onunload = (function(previous){ return function(){ window.clearAllTimeouts(); keep.list = {}; previous && previous.call(window); }; }(window.onunload)); })({});
turn on
Just put the above in js file and include it on your page using a regular script tag, the code does not need to be called in any way:
<script src="timeouts.js"></script>
Using
Obviously, this should be used as a regular setTimeout
call, but now you have additional methods that should provide more flexibility.
var tid = setTimeout( function(){ alert('OK Computer') }, 2000 );
For example, you can cancel the original and force the timeout to start earlier:
setTimeout( function(){ triggerTimeout( tid ); }, 500 );
Or you can update the timeout (make sure we remember the new returned tid):
setTimeout( function(){ tid = updateTimeout( tid, 5000 ); }, 500 );
You can also do the usual:
setTimeout( function(){ clearTimeout( tid ); }, 1000 );
Each of these methods is also available through tid
itself:
setTimeout( function(){ tid.trigger(); }, 1000 ); setTimeout( function(){ tid.update( 5000 ); }, 1000 ); setTimeout( function(){ tid.clear(); }, 1000 );
By default, this code prevents the use of setTimeout
with the string parameter, mainly because it is a much better coding style for passing functions, not strings. To change this, you can switch the following setting to true:
keep.settings = { eval: true };
However, this is not recommended.
There is also an additional advantage in order to exclude eval due to the fact that the code will use a normal function call to trigger a timeout, i.e. .apply()
. This means that any browser you use, you can pass arguments to the timeout function through setTimeout - which is usually not something that you can rely on cross-browser. eg:
setTimeout( function(a){ alert(a) }, 2000, 'Hello World' );