How can I pass a parameter to a setTimeout() callback?

    |
  • Added:
  • |
  • In: Wordpress

I have some JavaScript code that looks like:

function statechangedPostQuestion() { //alert("statechangedPostQuestion"); if (xmlhttp.readyState==4) { var topicId = xmlhttp.responseText; setTimeout("postinsql(topicId)",4000); } } function postinsql(topicId) { //alert(topicId); } 

I get a error that topicId is not defined Everything was working before i used the setTimeout() function.

I want my postinsql(topicId) function to be called after some time. What should i do?

This Question Has 18 Answeres | Orginal Question | Zeeshan Rang

The easiest cross browser solution for supporting parameters in setTimeout:

setTimeout(function() { postinsql(topicId); }, 4000) 

If you don't mind not supporting IE 9 and lower:

setTimeout(postinsql, 4000, topicId); 

setTimeout desktop browser compatibility

setTimeout mobile browser compatibility

https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout

After doing some research and testing, the only correct implementation is:

setTimeout(yourFunctionReference, 4000, param1, param2, paramN); 

setTimeout will pass all extra parameters to your function so they can be processed there.

The anonymous function can work for very basic stuff, but within instance of a object where you have to use "this", there is no way to make it work. Any anonymous function will change "this" to point to window, so you will lose your object reference.

Some answers are correct but convoluted.

I am answering this again, 4 years later, because I still run into overly complex code to solve exactly this question. There IS an elegant solution.

First of all, do not pass in a string as the first parameter when calling setTimeout because it effectively invokes a call to the slow "eval" function.

So how do we pass in a parameter to a timeout function? By using closure:

settopic=function(topicid){ setTimeout(function(){ //thanks to closure, topicid is visible here postinsql(topicid); },4000); } ... if (xhr.readyState==4){ settopic(xhr.responseText); } 

Some have suggested using anonymous function when calling the timeout function:

if (xhr.readyState==4){ setTimeout(function(){ settopic(xhr.responseText); },4000); } 

The syntax works out. But by the time settopic is called, i.e. 4 seconds later, the XHR object may not be the same. Therefore it's important to pre-bind the variables.

Note that the reason topicId was "not defined" per the error message is that it existed as a local variable when the setTimeout was executed, but not when the delayed call to postinsql happened. Variable lifetime is especially important to pay attention to, especially when trying something like passing "this" as an object reference.

I heard that you can pass topicId as a third parameter to the setTimeout function. Not much detail is given but I got enough information to get it to work, and it's successful in Safari. I don't know what they mean about the "millisecond error" though. Check it out here:

http://www.howtocreate.co.uk/tutorials/javascript/timers

I recently came across the unique situation of needing to use a setTimeout in a loop. Understanding this can help you understand how to pass parameters to setTimeout.

Method 1

Use forEach and Object.keys, as per Sukima's suggestion:

var testObject = { prop1: 'test1', prop2: 'test2', prop3: 'test3' }; Object.keys(testObject).forEach(function(propertyName, i) { setTimeout(function() { console.log(testObject[propertyName]); }, i * 1000); }); 

I recommend this method.

Method 2

Use bind:

var i = 0; for (var propertyName in testObject) { setTimeout(function(propertyName) { console.log(testObject[propertyName]); }.bind(this, propertyName), i++ * 1000); } 

JSFiddle: http://jsfiddle.net/MsBkW/

Method 3

Or if you can't use forEach or bind, use an IIFE:

var i = 0; for (var propertyName in testObject) { setTimeout((function(propertyName) { return function() { console.log(testObject[propertyName]); }; })(propertyName), i++ * 1000); } 

Method 4

But if you don't care about IE < 10, then you could use Fabio's suggestion:

var i = 0; for (var propertyName in testObject) { setTimeout(function(propertyName) { console.log(testObject[propertyName]); }, i++ * 1000, propertyName); } 

Method 5 (ES6)

Use a block scoped variable:

let i = 0; for (let propertyName in testObject) { setTimeout(() => console.log(testObject[propertyName]), i++ * 1000); } 

Though I would still recommend using Object.keys with forEach in ES6.

I think you want:

setTimeout("postinsql(" + topicId + ")", 4000); 

In modern browsers, the "setTimeout" receives a third parameter that is sent as parameter to the internal function at the end of the timer.

Example:

var hello = "Hello World"; setTimeout(alert, 1000, hello);

More details:

@Jiri Vetyska thanks for the post, but there is something wrong in your example. I needed to pass the target which is hovered out (this) to a timed out function and I tried your approach. Tested in IE9 - does not work. I also made some research and it appears that as pointed here the third parameter is the script language being used. No mention about additional parameters.

So, I followed @meder's answer and solved my issue with this code:

$('.targetItemClass').hover(ItemHoverIn, ItemHoverOut); function ItemHoverIn() { //some code here } function ItemHoverOut() { var THIS = this; setTimeout( function () { ItemHoverOut_timeout(THIS); }, 100 ); } function ItemHoverOut_timeout(target) { //do something with target which is hovered out } 

Hope, this is usefull for someone else.

How i resolved this stage ?

just like that :

setTimeout((function(_deepFunction ,_deepData){ var _deepResultFunction = function _deepResultFunction(){ _deepFunction(_deepData); }; return _deepResultFunction; })(fromOuterFunction, fromOuterData ) , 1000 ); 

setTimeout wait a reference to a function, so i created it in a closure, which interprete my data and return a function with a good instance of my data !

Maybe you can improve this part :

_deepFunction(_deepData); // change to something like : _deepFunction.apply(contextFromParams , args); 

I tested it on chrome, firefox and IE and it execute well, i don't know about performance but i needed it to be working.

a sample test :

myDelay_function = function(fn , params , ctxt , _time){ setTimeout((function(_deepFunction ,_deepData, _deepCtxt){ var _deepResultFunction = function _deepResultFunction(){ //_deepFunction(_deepData); _deepFunction.call( _deepCtxt , _deepData); }; return _deepResultFunction; })(fn , params , ctxt) , _time) }; // the function to be used : myFunc = function(param){ console.log(param + this.name) } // note that we call this.name // a context object : myObjet = { id : "myId" , name : "myName" } // setting a parmeter myParamter = "I am the outer parameter : "; //and now let's make the call : myDelay_function(myFunc , myParamter , myObjet , 1000) // this will produce this result on the console line : // I am the outer parameter : myName 

Maybe you can change the signature to make it more complient :

myNass_setTimeOut = function (fn , _time , params , ctxt ){ return setTimeout((function(_deepFunction ,_deepData, _deepCtxt){ var _deepResultFunction = function _deepResultFunction(){ //_deepFunction(_deepData); _deepFunction.apply( _deepCtxt , _deepData); }; return _deepResultFunction; })(fn , params , ctxt) , _time) }; // and try again : for(var i=0; i<10; i++){ myNass_setTimeOut(console.log ,1000 , [i] , console) } 

And finaly to answer the original question :

 myNass_setTimeOut( postinsql, 4000, topicId ); 

Hope it can help !

ps : sorry but english it's not my mother tongue !

As there is a problem with the third optonal parameter in IE and using closures prevents us from changing the variables (in a loop for example) and still achieving the desired result, I suggest the following solution.

We can try using recursion like this:

var i = 0; var hellos = ["Hello World1!", "Hello World2!", "Hello World3!", "Hello World4!", "Hello World5!"]; if(hellos.length > 0) timeout(); function timeout() { document.write('<p>' + hellos[i] + '<p>'); i++; if (i < hellos.length) setTimeout(timeout, 500); } 

We need to make sure that nothing else changes these variables and that we write a proper recursion condition to avoid infinite recursion.

My answer:

setTimeout((function(topicId) { return function() { postinsql(topicId); }; })(topicId), 4000); 

Explanation:

The anonymous function created returns another anonymous function. This function has access to the originally passed topicId, so it will not make an error. The first anonymous function is immediately called, passing in topicId, so the registered function with a delay has access to topicId at the time of calling, through closures.

OR

This basically converts to:

setTimeout(function() { postinsql(topicId); // topicId inside higher scope (passed to returning function) }, 4000); 

EDIT: I saw the same answer, so look at his. But I didn't steal his answer! I just forgot to look. Read the explanation and see if it helps to understand the code.

This is a very old question with an already "correct" answer but I thought I'd mention another approach that nobody has mentioned here. This is copied and pasted from the excellent underscore library:

_.delay = function(func, wait) { var args = slice.call(arguments, 2); return setTimeout(function(){ return func.apply(null, args); }, wait); }; 

You can pass as many arguments as you'd like to the function called by setTimeout and as an added bonus (well, usually a bonus) the value of the arguments passed to your function are frozen when you call setTimeout, so if they change value at some point between when setTimeout() is called and when it times out, well... that's not so hideously frustrating anymore :)

Here's a fiddle where you can see what I mean - http://jsfiddle.net/thedavidmeister/7t2bV/

setTimeout(function() { postinsql(topicId); }, 4000) 

You need to feed an anonymous function as a parameter instead of a string, the latter method shouldn't even work per the ECMAScript specification but browsers are just lenient. This is the proper solution, don't ever rely on passing a string as a 'function' when using setTimeout() or setInterval(), it's slower because it has to be evaluated and it just isn't right.

UPDATE:

As Hobblin said in his comments to the question, now you can pass arguments to the function inside setTimeout using Function.prototype.bind()

Example:

setTimeout(postinsql.bind(null, topicId), 4000); 

Replace

 setTimeout("postinsql(topicId)", 4000); 

with

 setTimeout("postinsql(" + topicId + ")", 4000); 

or better still, replace the string expression with an anonymous function

 setTimeout(function () { postinsql(topicId); }, 4000); 

EDIT:

Brownstone's comment is incorrect, this will work as intended, as demonstrated by running this in the Firebug console

(function() { function postinsql(id) { console.log(id); } var topicId = 3 window.setTimeout("postinsql(" + topicId + ")",4000); // outputs 3 after 4 seconds })(); 

Note that I'm in agreeance with others that you should avoid passing a string to setTimeout as this will call eval() on the string and instead pass a function.

The answer by David Meister seems to take care of parameters that may change immediately after the call to setTimeout() but before the anonymous function is called. But it's too cumbersome and not very obvious. I discovered an elegant way of doing pretty much the same thing using IIFE (immediately inviked function expression).

In the example below, the currentList variable is passed to the IIFE, which saves it in its closure, until the delayed function is invoked. Even if the variable currentList changes immediately after the code shown, the setInterval() will do the right thing.

Without this IIFE technique, the setTimeout() function will definitely get called for each h2 element in the DOM, but all those calls will see only the text value of the last h2 element.

<script> // Wait for the document to load. $(document).ready(function() { $("h2").each(function (index) { currentList = $(this).text(); (function (param1, param2) { setTimeout(function() { $("span").text(param1 + ' : ' + param2 ); }, param1 * 1000); })(index, currentList); }); </script> 

I know it's old but I wanted to add my (preferred) flavour to this.

I think a pretty readable way to achieve this is to pass the topicId to a function, which in turn uses the argument to reference the topic ID internally. This value won't change even if topicId in the outside will be changed shortly after.

var topicId = xmlhttp.responseText; var fDelayed = function(tid) { return function() { postinsql(tid); }; } setTimeout(fDelayed(topicId),4000); 

or short:

var topicId = xmlhttp.responseText; setTimeout(function(tid) { return function() { postinsql(tid); }; }(topicId), 4000); 

this works in all browsers (IE is an oddball)

setTimeout( (function(x) { return function() { postinsql(x); }; })(topicId) , 4000); 

Hobblin already commented this on the question, but it should be an answer really!

Using Function.prototype.bind() is the cleanest and most flexible way to do this (with the added bonus of being able to set the this context):

setTimeout(postinsql.bind(null, topicId), 4000); 

For more information see these MDN links:
https://developer.mozilla.org/en/docs/DOM/window.setTimeout#highlighter_547041 https://developer.mozilla.org/en/docs/JavaScript/Reference/Global_Objects/Function/bind#With_setTimeout


Search
I am...

Sajjad Hossain

I have five years of experience in web development sector. I love to do amazing projects and share my knowledge with all.

NEED HOSTING?
Are you searching for Good Quality hosting?
You can Try It!
Connect Social With PHPAns
Top