nicholaswmin
nicholaswmin

Reputation: 22989

Delay a For Loop

I am trying to create a FOR loop that removes an element every 1000ms instead of rushing instantaneously through the array and perform the operations.

I am doing this for reasons of performance since going normally through the loop freezes my UI.

 function removeFunction (i,selected) {
    selected[i].remove(); 
    }

function startLoop() {
        var selected = paper.project.selectedItems;
        for (var i = 0; i < selected.length; i++) {
            if (selected[i].name === "boundingBoxRect") continue;
          setTimeout(removeFunction(i,selected),1000)

        }

    }

It seems that the selected[i].remove() method is getting called without any delay. Why is that? Since I have set a Timeout of 1000ms shouldn't the items get removed with 1000ms interval between each?

Note

In the code above, I am skipping an item called boundingBoxRect since I don't want to remove that. Just stating this so there is no confusion

Upvotes: 1

Views: 90

Answers (2)

T.J. Crowder
T.J. Crowder

Reputation: 1075625

It seems that the selected[i].remove() method is getting called without any delay. Why is that?

Because that's what you're telling it to do. setTimeout(removeFunction(i,selected),1000) calls removeFunction immediately and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.

You can get the effect you want by using a builder function:

setTimeout(buildRemover(i,selected),1000);

...where buildRemover is:

function buildRemover(index, array) {
    return function() {
        removeFunction(index, array);
    };
}

Note how buildRemover creates a function that closes over the index and array variables. Then it returns a reference to that function, which is what gets scheduled via setTimeout. When the timeout occurs, that generated function is called, and it calls removeFunction with the appropriate values.

You can also do something similar using ES5's Function#bind:

setTimeout(removeFunction.bind(null, i, selected),1000);

Function#bind returns a new function that, when called, will call the original (removeFunction above) use the given this value (null in our example) and arguments.

Upvotes: 1

James Donnelly
James Donnelly

Reputation: 128836

Simply turn it into a recursive function:

function removeFunction (i, selected) {
    // If i is equal to the array length, prevent further execution
    if (i === selected.length)
        return;

    // Remove ith child of selected array
    selected[i].remove();

    // Trigger same function after 1 second, incrementing i
    setTimeout(function() {
        removeFunction(++i,selected)
    }, 1000);
}

// Trigger the function to begin with, passing in 0 as i
removeFunction(0, paper.project.selectedItems);

Here's a JSFiddle demo (using console.log instead of selected[i].remove(), as you haven't provided a definition for that function);

Upvotes: 3

Related Questions