Mostafa Mekawy Mostafa Mekawy - 1 year ago 108
CSS Question

setTimeout function completely freezes any output?

I've been trying to create a Nav using jQuery, and part of it is rotating a gear PNG, and I've looked here and found someone describing how to do it without a plugin, but i modified their code so instead of changing angle every click i changed it so that it changes angle from 0 to 180 with delay between each step hence the setTimeout function. anyhow here's the code.


<!DOCTYPE html>

<meta charset="utf-8">
<script src="" integrity="sha256- 16cdPddA6VdVInumRGo6IbivbERE8p7CQR3HzTBuELA=" crossorigin="anonymous"> </script>
<script src="" integrity="sha256-T0Vest3yCU7pafRw9r+settMBX6JkKN06dqBnpQ8d30=" crossorigin="anonymous"></script>
<script src="main.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="mystyle.css">

<div id="header">
<div id="gear">
<img src="gear.png">



#header {
position: relative;

#gear {
position: absolute;
left: 50px;


$(document).ready(function() {

var rotDeg = 0;
var rotEnd = 180;

jQuery.fn.rotate = function(degrees) {
$(this).css({'-webkit-transform' : 'rotate('+ degrees +'deg)',
'-moz-transform' : 'rotate('+ degrees +'deg)',
'-ms-transform' : 'rotate('+ degrees +'deg)',
'transform' : 'rotate('+ degrees +'deg)'});
return $(this);
$('#gear').click(function() {
while(rotDeg <= rotEnd) {
setTimeout(function() {
rotDeg += 1;
}, 34);

Answer Source

setTimeout will schedule a function to run after at least the given number of milliseconds, but only when no other code is running. However, your while loop will never end since rotDeg is not changed inside the loop (since the callback is never called), and since this causes an infinite loop, there's never any point where there's no other code running, and the scheduled callback can be run.

The solution is to drop the while loop, and instead do something like a recursively called function:

$('#gear').click(function() {
  var that = this; // <-- this is important, since we cannot use `this`
                   // inside an inner function, like you did in your
                   // original code

  const func = function() {
    // note that the condition here is negated, compared to the while loop
    if(rotDeg > rotEnd)

    // do stuff
    rotDeg += 1; 
    $(that).rotate(rotDeg); // <-- again, note `that` that we defined above

    // do next "iteration" after at least 34 ms
    setTimeout(func, 34);


Note also that you cannot use this inside the inner functions here; the reasons for that are explained in detail in this answer.