1

I am doing some simple animation in Javascript. In light of the recent earthquake on the East Coast, I have implemented an earthquake effect whereby a table of information jostles around for a while when you click a button. I want the jostling to start out strong, and then peter out.

I have a utility function that repeatedly calls another function at a set interval. Then it calls a second function when it is all done calling the first function a bunch of times. This is so that you can schedule something to occur when the animation is over. Here is the code for it:

function countIterate(timeout, count, func1, func2)
{
    if (count > 0) {
        func1();
        setTimeout(function() { countIterate(timeout, --count, func1, func2); }, timeout);
    }
    else
        func2();
}

Here is the earthquake routine:

function earthQuake()
{
    console.log("earthQuake()");
    $("table").css("position", "relative");

    var quake = function(magnitude)
    {
        var top = Math.floor(Math.random() * (2 * magnitude + 1)) - magnitude;
        var left = Math.floor(Math.random() * (2 * magnitude + 1)) - magnitude;
        $("table").css("top", top).css("left", left);
    }

    var func = new Array();
    func[0] = function() {};

    for (var i = 1; i <= 4; i++) {
        func[i] = function() { countIterate(35, 40, function() { quake(i); }, func[i-1]); };
        console.log(func[i]);
    }

    func[4]();
}

Unfortunately, I am getting an infinite earthquake loop.

If I hard-code things instead of the for loop:

var func0 = function() {};
var func1 = function() { countIterate(35, 40, function() { quake(1); }, func0); };
var func2 = function() { countIterate(35, 40, function() { quake(2); }, func1); };
var func3 = function() { countIterate(35, 40, function() { quake(3); }, func2); };
var func4 = function() { countIterate(35, 40, function() { quake(4); }, func3); };
func4();

it works fine. But this is an ugly solution.

By the way, here is the console.log() output from the first (more elegant, but broken) solution:

function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }

If there is some library that will take care of this sort of thing, please let me know, but I want to get this version working anyway as a learning experience.

Jeremy
  • 1
  • 85
  • 340
  • 366
Sam
  • 81
  • 2
  • 7
  • can you show a [jsfiddle demo](http://jsfiddle.net) of the ugly solution so we can get an idea of what you are trying to do? – Ibu Aug 25 '11 at 03:07

1 Answers1

0

The function:

function() { countIterate(35, 40, function() { quake(i); }, func[i-1]); }

is always executed with i = 5 because by the time func[4](); is reached the for loop has already completed. This can be easily shown to be the problem by binding the value of i to the function by returning the function from another:

(function(i) {
    return function() {
        countIterate(35, 40, function() { quake(i); }, func[i-1]);
    };
}(i))

For further explanation, you can refer to answers to the many other "for loop problem" questions that have been asked here, including the top answer for Javascript infamous Loop issue?.

Community
  • 1
  • 1
PleaseStand
  • 31,641
  • 6
  • 68
  • 95