...

View Full Version : Recursive increment & setTimeout?



swmr
02-09-2004, 07:05 AM
Why does my function run out of memory if I don't use setTimeout(..., 0)?

I can see what's happening:

if the loop number is greater than 459, the error occurs, and without a timeout, the output moves at light-speed.

However, I don't understand what's actually going on here...

why does the "out of memory" error occur, and what does a zero timeout-delay do to correct the problem?


<html>
<head>
<title>-</title>

<script type="text/JavaScript">

var i, n, stop;

function Recurse(oForm){
i++;
oForm.progress.value = i;
oForm.percentage.value = Math.floor(((i / n) * 100)) + "%";
i < n && +stop != 1 ? setTimeout(function (){Recurse(oForm);}, 0) : controlUpdate("Complete");
}

function controlUpdate(callString){
oForm = document.RecTest;

switch(callString){

case "Start" :
n = new Number(oForm.test.value);
if(isNaN(n) || n < 1){
alert("loop value must be a number greater than 0. ");
}
else{
i = 0;
stop = false;
oForm.cancel.disabled = 0;
oForm.start.disabled = 1;
oForm.test.disabled = 1;
Recurse(oForm);
}
break;

case "Cancel" :
stop = true;
oForm.rs.disabled = 0;
oForm.cancel.disabled = 1;
break;

case "Complete" :
oForm.rs.disabled = 0;
break;

case "Reset" :
oForm.cancel.disabled = 1;
oForm.start.disabled = 0;
oForm.test.disabled = 0;
oForm.rs.disabled = 1;
break;}
}
</script>

<style type="text/css">
form{
text-align:center
}
input{
text-align:center;vertical-align:middle
}
label{
padding-left:10px;padding-right:5px
}
</style>

</head>
<body>

<form name="RecTest" onreset="controlUpdate('Reset')">
<label>i :</label><input name="progress" size="10" readonly>
<label>% :</label><input name="percentage" size="10" readonly>
<br><br>
<label>loop :</label><input name="test" value="500" size="10" maxlength="5">
<input name="start" type="button" value="Start" onclick="controlUpdate('Start')">
<input name="cancel" type="button" value="Cancel" disabled onclick="controlUpdate('Cancel')">
<input name="rs" type="reset" disabled>
</form>

</body>
</html>

glenngv
02-09-2004, 07:35 AM
liorean can explain this better but I might as well share that Javascript recursion has maximum depth (http://w3log.server-wg.de/000055.html). By using setTimeout or setInterval, you can make it an endless loop.

swmr
02-09-2004, 07:55 AM
Yeah, that's interesting... I wonder what the difference is between maximum depth, or memory (or whatever), as it applies to the Function Object vs. the For/While Statements... and, why those statements won't show output results until they have finished looping.

andi
02-09-2004, 09:30 AM
hi,
what the javascript interpreter does if it detects true recursivnes, like you use it here, is pushing the function object on the execution stack each time it gets called.
this is why the memory usage rises so rapidly and it stops when the interpreter needs more memory then it reserves itself. thats why the results vary for different browsers because there is no definition how much memory a javascript interpreter should use and each browser handles this differently.
in some languages there is a technique called "tail call elimination", where the interpreter detects and removes unmodified objects from the stack, so that you can recurse endlessly but this is not covered in the ecma-scriŁt spec and there exists only one javascript interpreter which can handle this (http://wiki.cocoondev.org/Wiki.jsp?page=RhinoWithContinuations).
so as long as you don't know how often you have to recurse or supply a mechanism to break out of the recursion, usage setTimeout() or
setInterval() is better as it uses always the same function object but with the defined delay between the calls.

swmr
02-09-2004, 07:23 PM
Thank you for explaining that. :)

liorean
02-09-2004, 07:41 PM
There is one thing to it, though: A recursive function returns the results to the context. A function called from a setTimeout or setInterval doesn't. That may or may not limit the possibility of using setTimeout or setInterval for this kind of job. In general an iterative statement such as the do..while loop, the while loop or the for loop is the better choice.

swmr
02-09-2004, 08:42 PM
Thanks liorean, would you happen to have any code snippets that illustrate the difference there?

From what I gather, recursion is the only way to update a control - such as a progress bar - for each incremental step... so it seems that's where it would be required.

glenngv
02-10-2004, 03:25 AM
FWIW...


<html>
<head>
<title>-</title>

<script type="text/JavaScript">

var i, n, stop, timer;

function Recurse(oForm){
i++;
oForm.progress.value = i;
oForm.percentage.value = Math.floor(((i / n) * 100)) + "%";
if (i >= n || stop) controlUpdate("Complete");
}

function controlUpdate(callString){
oForm = document.RecTest;

switch(callString){

case "Start" :
n = new Number(oForm.test.value);
if(isNaN(n) || n < 1){
alert("loop value must be a number greater than 0. ");
}
else{
i = 0;
stop = false;
oForm.cancel.disabled = 0;
oForm.start.disabled = 1;
oForm.test.disabled = 1;
timer = setInterval(function(){Recurse(oForm)},1);
}
break;

case "Cancel" :
clearInterval(timer);
stop = true;
oForm.rs.disabled = 0;
oForm.cancel.disabled = 1;
break;

case "Complete" :
clearInterval(timer);
oForm.rs.disabled = 0;
break;

case "Reset" :
oForm.cancel.disabled = 1;
oForm.start.disabled = 0;
oForm.test.disabled = 0;
oForm.rs.disabled = 1;
break;}
}
</script>

<style type="text/css">
form{
text-align:center
}
input{
text-align:center;vertical-align:middle
}
label{
padding-left:10px;padding-right:5px
}
</style>

</head>
<body>

<form name="RecTest" onreset="controlUpdate('Reset')">
<label>i :</label><input name="progress" size="10" readonly>
<label>% :</label><input name="percentage" size="10" readonly>
<br><br>
<label>loop :</label><input name="test" value="500" size="10" maxlength="5">
<input name="start" type="button" value="Start" onclick="controlUpdate('Start')">
<input name="cancel" type="button" value="Cancel" disabled onclick="controlUpdate('Cancel')">
<input name="rs" type="reset" disabled>
</form>

</body>
</html>

swmr
02-10-2004, 07:38 AM
stop = false; if(!stop)... -- that's good to know! :thumbsup:


Thanks for the example, glenngv.

Is there an advantage to using setInterval instead of setTimeout, here, or was that just to demonstrate the alternative?

glenngv
02-10-2004, 07:47 AM
If you want to call the function repetitively, then setInterval is better because you don't have to call the function again inside that function. But if you want to call the function only once with a delay, then use setTimeout.

swmr
02-10-2004, 09:05 AM
Ah, setInterval does sound more appropriate, but how can I know how long a function will take to execute: would there be a possibility of calling it again too soon?

Also, what happens differently when a function is called from within itself?

glenngv
02-10-2004, 10:21 AM
setInterval will wait for the function to finish execution before executing the function again unless there is setTimeout inside in which the setInterval function is re-executed regardless the setTimeout function has finished execution or not. Hope that makes sense.

According to this test (http://dhtmlkitchen.com/dhtml/performance/animation/setTimeout.html), setTimeout is faster than setInterval.

swmr
02-10-2004, 08:07 PM
Yeah, that made sense; thank you. :)



EZ Archive Ads Plugin for vBulletin Copyright 2006 Computer Help Forum