Reputation: 145
I know what the problem I am experiencing is, I am just having a hard time finding a work around. I was wondering if anyone had experienced something like this, and what kinda of solution they implemented.
I have a list system with pending repairs, and I want repairs that are late to blink black and red. It's possible that there are multiple repairs in this list that are late.
here is my function:
function setblink(id) {
var elm = document.getElementById(id);
if (elm.color == "red"){
elm.color = "black";
}
else{
elm.color = "red";
}
setTimeout(setblink(id),500);
}
I have an array of "id's" for the items that need to be blinking called repsToBlink.
I get the set blink intervals running for each of these repairs by running the following code, which puts them in a recursive loop.
for(var x in repsToBlink){
setTimeout(setblink(repsToBlink[x]),500);
}
How can I get this code doing the same thing, without causing a stack overflow?
Thanks!
Upvotes: 0
Views: 295
Reputation: 77420
setblink(id)
calls the function immediately. A stack overflow is a symptom of immediate, rather than delayed, execution since setTimeout
schedules execution for later so the future invocation won't get pushed on the current call stack.
Since setblink
takes an argument, wrap it in a nullary anonymous function for lazy evaluation.
function setblink(id) {
var elm = document.getElementById(id);
if (elm.color == "red"){
elm.color = "black";
}
else{
elm.color = "red";
}
setTimeout(function () {setblink(id)},500);
}
for (var x in repsToBlink){
(function (id) {
setTimeout(function () {setblink(id)},500);
})(repsToBlink[x]);
}
The code calls for more improvements.
If repsToBlink
is an array, you should loop over the integer indices of repsToBlink
(for (...;...;...)
), not the properties (for ... in
). However, if instead you were to use an object with the ids an indices (rather than values), for ... in
would be appropriate.
The above fires a separate timer for each id
(which could overwhelm the browser). By moving the loop to a function, which becomes the only function to be scheduled, only a single timer is required.
Since you're running a function periodically, setInterval
is more appropriate.
Whenever you remove and id from repsToBlink
, check whether there are any remaining; if not, cancel the interval.
(function () {
var repsToBlink, repCount=0, blinkInterval;
function startBlinking(ids) {
addRepsToBlink(ids);
if (! blinkInterval) {
blinkInterval = setTimeout(blinkAll, 500);
}
}
function addRepsToBlink(ids) {
for (var i=0; i<ids.length; ++i) {
addRep(ids[i]);
}
}
function addRep(id) {
if (! id in repsToBlink) {
++repCount;
repsToBlink[ids[i]] = true;
}
}
function removeRep(id) {
if (id in repsToBlink) {
delete repsToBlink[id];
--repCount;
if (!repCount) {
clearInterval(blinkInterval);
blinkInterval=0;
}
}
}
function blinkAll() {
for (id in repsToBlink) {
blink(id);
}
}
function blink(id) {
var elm = document.getElementById(id);
if (elm.color == "red"){
elm.color = "black";
} else {
elm.color = "red";
}
}
window.startBlinking = startBlinking;
window.addRepsToBlink = addRepsToBlink;
window.addRep = addRep;
window.removeRep = removeRep;
})();
Upvotes: 2
Reputation: 66693
You need to change your setTimeout from
setTimeout(setblink(id),500);
to:
setTimeout(function() { setblink(id) },500);
setTimeout()
expects a function to be passed as parameter, whereas you are passing the result of the function.
Upvotes: 3
Reputation: 6124
Your problem is that setTimeout
is being called in a global context. That, and you're instantly calling the function.
The interpreter, when it gets to this code:
setTimeout(setblink(id),500);
is calling the setblink
function immediately, assuming that that function's return value is what the timeout is supposed to call. This causes a stack overflow, because that is a recursive function.
To fix, this, wrap the function that setTimeout is to call inside a function(){}
.
Upvotes: 1