Reputation: 2464
I know that in the following html + javascript/jquery code, the three items will all fade in simultaneously. What I don't really know is why.
<ul>
<li id='foo'></li>
<li id='bar'></li>
<li id='baz'></li>
</ul>
<script type="text/javascript">
$('#foo').text('foo').hide().fadeIn(5000);
$('#bar').text('bar').hide().fadeIn(5000);
$('#baz').text('baz').hide().fadeIn(5000);
</script>
My understanding is that each fadeIn effect would get added to the event loop FIFO queue. But don't all events in this queue get handled synchronously and each event handler runs to completion until the next in the queue is called? If so, shouldn't each fadeIn effect happen one at a time? Obviously, this is not what is happening (and it wouldn't be good if it did).
Instead is something like this happening? foo fades in for 16ms (assuming 60hz repainting) and then the handler calls itself thus putting itself back on the queue; then bar fades in for 16ms, then its handler calls itself; then baz fades in and its handler calls itself. This cycle continues for 5000ms.
Something like this would make sense, but I am just guessing and would like to know for sure how it works.
Upvotes: 2
Views: 144
Reputation: 21166
Why?
setTimeout
and setInterval
are asynchronous operations... The fadeIn function supplied by jquery uses the setTimeout function to move the animation along one frame... then allows the rest of the code to try and move on a bit, and then it gets called again to progress the frame a little more.
Example code to explain:
Imagine this:
var currentAnimationFrame = 0;
function AnimateABit()
{
currentAnimationFrame++;
if( currentAnimationFrame < 10 )
{
setTimeout( function(){
AnimateABit();
}, 1000 );
}
}
function fadeIn()
{
AnimateABit();
}
fadeIn();
Imagine the above code is how jquery works.
It might declare a global variable to keep track of which frame we are on, we then call fadeIn()
which calls the AnimateABit
function... increases the animation frame by 1, then tells the code to wait 1000 (1 second) and then run the AnimateABit
function again...
While the wait 1000 part is called, the rest of the code is allowed to resume (asynchronous).
How?
Usually, this asynchronous ability is due to firing off new processor threads which means code can run simultaneously... unfortunately Javascript isn't quite that clever but it does get the job done in some convoluted way which i forget how it works :)
Solution to your problem
jquery gives you a way of signalling when the animation code has completed (a call back function), it works like this:
$().fadeIn( 5000, function() {
// The animation has finished, now let's do the next bit:
$().fadeIn( 5000, function() {
// And so on...
} );
});
Future read:
https://msdn.microsoft.com/en-us/library/windows/apps/hh700330.aspx
Be mindful of ajax calls as well, these too are asynchronous.
Upvotes: 3
Reputation: 206508
"I know that in the following html + javascript/jquery code, the three items will all fade in simultaneously. What I don't really know is why."
Cause you started all three animations simultaneously:
#foo >> start with your stuff
#bar >> start with your stuff
#baz >> start with your stuff
it's like your expecting title
and fooBar
to be executed after some animations:
$('#foo').fadeIn(10000);
// Nope, JS is syncronous and we don't wait for jQuery animations :)
$("#title").text("TITLE"); //< "I'm not going to wait 10000ms sir!"
function fooBar(){
// other important code here
}
fooBar(); //< "I have no time too :)"
jQuery people knew that one would like to Do this and Do that once an animation ends, therefore most animating Methods like .animate()
, fadeTo()
, fadeIn()
, fadeToggle()
etc. have an argument called:
/* fadeIn example: */ .fadeIn([time] [, callback])
where once jQuery finishes the animation will trigger whatever function you use as callback:
$("#foo").fadeIn(10000, function(){
// do something after 10s
});
Another solution is to use the jQuery's .delay()
method, which is than stack in the current animation.queue
bound to your element:
$("#foo").hide().delay(1000).fadeIn(1000);
and does:
STYLE display:none
to #foo
delay
of 1000ms to animation queuefadeIn
to animation queueFor multiple elements you can get their index number (0,1,2...) and multiply that number by the desired delay time:
$("#foo, #bar, #baz").hide().each(function(idx, el){
$(el).delay(idx*700).fadeIn(1000);
});
// All effects above will start at the same time,
// the only difference is the increased `.delay(index*time)`
// bound to every element animation queue
Upvotes: 0
Reputation: 1018
Yes, it's pretty much as you've described it. There's a FIFO queue of JS functions that get executed in a synchronous, blocking fashion.
For your specific case of animations, they appear to run simultaneously because each animation is split into steps that are then interleaved with each other with the use of setTimeout or requestAnimationFrame.
Upvotes: 0
Reputation: 895
If you want to relatively easily analyse how it's working (step by step), I would advise to you to use Timeline from Chrome DevTools, which is pretty handy tool to this kind of stuff.
Last, but not least, Philip Roberts gave a great talk at JSConf EU, which gives a deep understanding on how the event loop works, which you might find useful to solve you issue.
Upvotes: 0
Reputation: 171690
You can use delay()
to offset each animation. delay()
will get put into each elements animation queue
.
var duration = 2000;
$.each(['foo','bar','baz'], function(i, item){
$('#'+item).text(item).hide().delay( i * duration).fadeIn(duration)
});
Reference: delay() docs
Upvotes: 0
Reputation: 85573
Because all javascript code are synchronous except like setTimeout function.
If you want to run your code one after another then you should pass a callback like below:
$('#foo').text('foo').hide().fadeIn(5000,function(){
$('#bar').text('bar').hide().fadeIn(5000,function(){
$('#baz').text('baz').hide().fadeIn(5000);
});
});
Upvotes: 0