Just can’t seem to figure this out, how to prevent the outer loop continuing until the inner loop has executed using setTimeout:
function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
$('t'+i).className = 'show';
cl('t'+i);
for (var j=0; j<50; j++){
cl('b'+i+'='+j+'%');
setTimeout(function(){ $('b'+i).style.width = j+'%';},200);
}
}
This little bit of code is supposed to first make elements t0 visible, and then set the width of another element b0 in 1% steps with a time interval of 200ms, and then continue with t1,b1, t2,b2 etc etc.
But there’s no 200ms delay, the whole code executes immediately.
— EDIT —
I have not explained very well, here’s what I want to do:
1. show element Ax
2. increase element Bx width by 1% every 200ms until 50%
3. wait until element Bx reaches 50% before continuing
4. increment x
5. goto 1
Two problems:
iandjvaluesThe timeout functions see the wrong
iandjvaluesThe main problem is that the function you’re passing into
setTimeouthas an enduring reference to theiandjvariables, not copies of them as of when the function was created. That means all of the functions will see the value ofiandjas of when they run, which will be6and50respectively. This is called “closing over” those variables (and the function is called a “closure”).The usual way to fix this is to create the functions in a way that they close over something that doesn’t change. There are a couple of ways to do that; my favorite is to use a factory function:
Now we call
makeHandler, and it returnns us a function that closes overivalueandjvalue, which won’t change. Or a refinement of the above which lets us dispose of the maker function when we’re done with it:If you can rely on ES5 features (because of the environment you’re targeting, or because you’ve included an ES5 shim), you can get much the same effect using the new
Function#bind.Function#bindcreates a new function just likemakeHandlerdoes, but there’s always the possibility the engine can optimize a bit:The first argument to
bindis whatthisshould be in the function; in our case we don’t care, so I’ve specifiedundefined(which means the function will getthisreferencing the global object —window, on browsers — unless this is strict mode code, in which casethiswill actually beundefined).They all run at the same time (200ms later)
All of your functions are being scheduled to run 200ms after the above code. And so they will. 🙂 If you want to space them out, you’ll want to increase that 200ms for each call to
setTimeout. We can just multiply byiandj:Now the first one will run after 200ms, the second one 200ms later, etc. The whole thing will take about a minute to complete. This assumes you want the first element to grow, then the next, then the next, as opposed to all six of them growing in parallel with each other.
Alternately, you might want to have each function call its successor. That’s probably what I’d do. So rather than scheduling 300 function calls, just schedule one and when it happens, schedule the next:
In the above, I’ve also moved these lines:
…into the delayed code, which I suspected was appropriate.
More to explore: