Jump to content

Calling functions with live argument in setTimeout


Jack McKalling
 Share

Recommended Posts

Hello,There is this script, that I want to execute each interval. I know there is a specific interval function, but lets say I am using window's setTimeout method.While doing this, I want the series of intervals to run on an individual (live) timer object. This is because I need to be able to run several of these series at the same time, and if there is only one global object for the setTimeout to run on, I cannot run another simultaneously from within the same custom function.What I mean with an individual and live object, is a variable that is declared within the function, and passed to the setTimeout function directly, so the code expression can recieve the variable itself. Not its value, but the variable itself (the actual object, presented by the integer in the example below). This can either be done by adding the expression of the variable to the expression of the codeargument of setTimeout, or just as the variable to the method reference when the codeargument is not quoted. Below is a demonstration of such an unquoted codeargument, it runs and inserts an increasing number in the page:

<html><head><script type="text/javascript">var int = 0;function timer(){//if (int == null) { int = 1; }int++;document.getElementById("test").innerHTML = "test: " + int;t=setTimeout(timer, 1000);}</script></head><body onload='timer()'><div id="test"></div></body></html>

When converting the here "int" variable to an argument for the setTimeout method, however, the timeout effect is lost. See here:

<html><head><script type="text/javascript">//var int = 0;function timer(int){if (int == null) { int = 1; }int++;document.getElementById("test").innerHTML = "test: " + int;t=setTimeout(timer(int), 1000);}</script></head><body onload='timer()'><div id="test"></div></body></html>

Both ways do work, but the latter one does not increase, it just shows the last number of the session immediately, always "13118". I presume this specific number has something to do with google Chrome's memory for javascript, but that is not important. The question is why suddenly the setTimeout function fails to interval this, when I add an argument to the method reference in its argument. It seems it is not a Chrome specific problem. What can I do to pass the actual object to the expressed method in setTimeout's argument, without putting the whole argument in quotes and inserting its value?I specifically would like to know this, as the quotes around the code expression, remove the ability to actually pass the variable, instead of passing the value of the variable. Passing the actual variable, makes it possible to pass the timer object itself, or so I hope, so that I can maintain the timerobject inside the method scope, and let it run independantly of other timers created with this custommade method.I hope someone can explain how this works! The ultimate goal is to have a method that can be run multiple times, and execute simultaneously without interfering eachother's timer object. If this cannot be done passing the timer object through setTimeout, I might look into things like the setInterval method instead, or something that makes individual timers unnessecary anyway.Thanks!

Link to comment
Share on other sites

The trick comes when you want to pass arguments to your function. setTimeout(timer(int), 1000); won't do it. This actually executes the timer function when the statement is read and passes the return value (nothing) to setTimeout, which of course will terminate the process. You could put the code in quotations -- "timer(int)" -- but I don't think the value of int will exist when the quoted string gets evaluated. (Well, it should, since it's global, but you want to understand a technique that works without global variables also.) What you need is an anonymous function. It lets you do both.This is what that looks like:

var t;function timer(i) {	i = i || 0; // a cleaner way of testing for a null value	// do something with i	t = setTimeout(function () {timer(i);}, 1000);	i++;}

Some people might write it like this:

t = setTimeout(function () {   timer(i);}, 1000);

Edited by Deirdre's Dad
Link to comment
Share on other sites

Yes! Thanks, that was what I was looking for. I know anonymous functions but totally forgot about them. I guess I wasn't really accustomed with them.So now I understand, you need to postpone the execution by wrapping it in a(n anonymous) function. Never thought about it!I already used an array to store the timerobjects instead, so I only need the index to 'pass' it to the next interval, but which is not an object, that needs not to be passed without quotes. However this anonymous function removes the global array need, so I'm using this instead. Thank you, now I understand :)PS: the simple increasing number ofcource was not the project I am working on, its sometihng much more complex, brought simple for you to see what was important :) Maybe you still remember from some time ago, I last visited here, I was building a website. I currently try to build a new design for that same thing.

Edited by Jack McKalling
Link to comment
Share on other sites

Ok, so I implemented the next step in my project, but obviously there is a speed problem in the animation. Reverting back to the example, now I have this:

<html><head><script type="text/javascript">var i;function timer(t){  i = i || 0;  i++;  document.getElementById("test").innerHTML = "test: " + i;  t = setTimeout(function() { timer(t); }, 1000);}</script></head><body><input type="button" value="Start" onclick="timer()" /><div id="test">test: 0</div><input type="button" value="Stop" onclick="clearTimeout(t)" /></body></html>

It works like sweet, but if you keep pressing the start button, new timer sessions are created that make it go faster. Naturally the passed timer object is not declared yet as it is being declared. I could not think about this before it worked, so, here is the update to the topicproblem.If you happen to bump the session, it goes faster. It seems I need the actual declaration of the setTimeout function live in the object before I can pass it. Is that even possible? Btw, not to mention because of this the stop button also got broken.Edit:

(...)but I don't think the value of int will exist when the quoted string gets evaluated(...)
*Just simply add the int to the quoted string rather than expressing it within it: "timer(\"" + int + "\")" Edited by Jack McKalling
Link to comment
Share on other sites

Since t is now a global, there is really no reason to pass it around. Anyway, what's happening is you are creating multiple timers. So it SEEMS that the speed is increasing, when really it isn't. The simplest thing would be to clear your timer right before you set it.This simplified code (a lot like your original) works a bit better:

var t;var i = 0;function timer(){	i++;	document.getElementById("test").innerHTML = "test: " + i;	if (t)	{		clearTimeout(t);	}	t = setTimeout(timer, 1000);}

Link to comment
Share on other sites

That's right, but as explained in my first post, I'm trying to get the timer nonglobal. I want to make them sort of anonymous, so I can have several different interval sessions at the same time, created by the same custom function. When it is global, that would be impossible, because each session uses the same object and thus interfere with another session; when one starts, the previous ends immediately.I know it only seems it is increasing, however I gladly understood I actually was initiating simultaneous timeouts at the same interval, so that the correct speeds got added to eachother. In fact, every time I start a session, they all fuse together.But ultimately, is it not possible to pass the timer around, or simply "store" the timeout in the timer before it starts so I can pass the timer with due timeout active? I conclude that the fact that the timeout is not yet active in the timer at time of passing it, is the cause the timeouts to fuse together.

Edited by Jack McKalling
Link to comment
Share on other sites

Could be a problem. In your original function, t is already global, since you do not declare it with the var keyword. If you DO declare it that way, then every call to start() will create a unique timer. It might actually not even work, since then the function would have t as an argument and t as an internal variable, and JavaScript may not like the re-declaration.Timer variables are typically global, so that timers can be cleared anywhere.A solution would be to assign your timers to an array, as in:var t = new Array();function () {t[t.length] = setTimeout ...}This way, every timer is unique. Another problem, though, is that when you create and cancel them, you would need a way to track the individual timers.The main thing is that individual timers should be assigned to unique variables. It's hard to suggest a good way to do that without knowing more about your application.

Link to comment
Share on other sites

I know it is completely possible with arrays, as I store them according to the ID of the session, which is known in every call.And I also know it is possible to redeclare the timer object, when you completely quote the argument, like:t = setTimeout("timer(\"" + something + "\")", 1000);This actually works perfectly, even though the timer object can now only hold one session simultaneously. The expression of the variable is not a problem here, the value gets evaluated before passing. Would not work with an object though.I guess, from what you're saying, I just cannot forward the same timer object to maintain a session within one function, where a register array would do the trick. Then the example would be the following:

<html><head><script type="text/javascript">var timers = new Array();function timer(timerID, i){  i = i || 0;  i++;  document.getElementById(timerID).innerHTML = "test: " + i;  timers[timerID] = setTimeout("timer(\"" + timerID + "\", \"" + i + "\")", 1000);}</script></head><body><input type="button" value="Start1" onclick='timer("test1")' /><input type="button" value="Stop1" onclick='clearTimeout(timers["test1"])' /><div id="test1">test: 0</div><div id="test2">test: 0</div><input type="button" value="Start2" onclick='timer("test2")' /><input type="button" value="Stop2" onclick='clearTimeout(timers["test2"])' /></body></html>

Pity is has to be an array, I have several timing accessoires in my design, all those global arrays outside the functions might clutter around.I consider this solved then, though what I wanted was just not possible. Unfortunately I cannot show the actual application, as it is in local development (in PHP version) and somewhat a surprice for my public website :) You'll get to see it when it goes live :)Thanks for your help, I think I now know more about anonymous method calling via setTimeout.

Edited by Jack McKalling
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...