6

With the help of the jQuery library and the offset() method it seems logical to think that by writing this simple code the element will gradually change position

for (i = 0; i < 900; i = i + .5) { 
    $('#moving-element').offset({ top: i })
}

The browser will stop for a while and will finally move the element to a position 900px apart from the top, no transition can be observed. Out of curiosity i wrote this :

for (i = 0; i < 900; i = i + .5) { 
    $('#moving-element').offset({ top: i }); 
    console.log(i)
}

to see that the console is outputting the succession of numbers fine, but it only offsets the element once the for loop is over.

Why is this not done gradually as the code is being executed?

Rory McCrossan
  • 331,213
  • 40
  • 305
  • 339
Mg Gm
  • 151
  • 1
  • 11
  • 4
    How long do you think a for loop takes? – Qantas 94 Heavy Aug 15 '14 at 13:31
  • 1
    That's just not how JS works. Almost all functions will return as soon as possible. So effectively you're telling the element to move 900 times in (way) less than a second. This is why you only see the last animation, and have to work with callbacks. – Martin Tournoij Aug 15 '14 at 13:31
  • Because browsers don't immediately update the DOM when it's changed by JavaScript. If they did, your typical browsing experience would be terribly slow. – Pointy Aug 15 '14 at 13:31
  • possible duplicate of [Single thread concept of JavaScript running in browser](http://stackoverflow.com/questions/16749664/single-thread-concept-of-javascript-running-in-browser) – tkone Aug 15 '14 at 13:31
  • I have quantified how long the loop takes, experimented with even smaller intervals with exact results.. i just need to understand how the language works, thankyou – Mg Gm Aug 15 '14 at 13:33
  • See my answer to this related question to understand the reason: http://stackoverflow.com/questions/19616477/does-javascript-process-using-an-elastic-racetrack-algorithm/19620041#19620041 – slebetman Aug 15 '14 at 13:53

2 Answers2

5

Because javascript is a single-threaded event modeled runtime (at least in current incarnations).

This means while you're running your for loop no other tasks can be running. This means accepting user input, updating the screen, etc. So the loop runs entirely through and then displays the final result.

tkone
  • 22,092
  • 5
  • 54
  • 78
  • 3
    JavaScript is single threaded, the browser is not. It could in theory repaint the window every time, there's just no reason to do so because that would end up being ridiculously slow. – Anthony Grist Aug 15 '14 at 13:35
  • 1
    @AnthonyGrist I think what tknoe means about 'updating the screen' is use the javascript to manipulate the dom. It seems it's not about the browser repaint. : ) – Tyler.z.yang Aug 15 '14 at 13:39
  • @AnthonyGrist you're right, the browser is. But create a while loop that never ends -- you will not be able to use the browser at all. Javascript execution blocks user-input. That's why the browser says "this script is taking 4eva! you want me to stop it?!" It's your only ability to stop it short of quitting the browser – tkone Aug 15 '14 at 13:41
5

The problem is that javascript is single-threaded, and it blocks the ui and event queue when processing. You need to use setTimeout or some other asynchronous flow-control mechanism to generate drawing tasks to stick into the event loop so the browser can do other things while doing your animation.

// Our animate function changes the offset, waits a while, then repeats
function animate(n) {
    // Change the offset to the passed in value
    $('#moving-element').offset( {top: n});

    // If the passed in value is equal to 900, stop exection
    if (n === 900)
        return;        

    // Otherwise, set a timer which will trigger our animate function again
    // after 10 milliseconds
    setTimeout(function() {
        // Increment n when passing it into our function
        animate(n++):
    }, 10);
}

// Call the function to begin with
animate(0);

Here is a JSFiddle example which will give you some idea of just how fast for loops are. Upon clicking the button, a for loop will begin execution, and only when it reaches its one millionth iteration will it display an alert on the screen.

tkone
  • 22,092
  • 5
  • 54
  • 78
James Donnelly
  • 126,410
  • 34
  • 208
  • 218
  • 1
    This only works because you're inserting a `setTimeout`. The for-loop does nothing here other than create and insert a bunch of instructions to run asynchronously in the js-event loop. However, this will resolve the problem. But his issue isn't because the for loop is fast, it's because javascript is single threaded and ui-blocking. – tkone Aug 15 '14 at 13:43