Javascript Delay Between Function Calls For Each Index In An Array
Solution 1:
OK, now that you've completely simplified the question, here's a generic array iterator function that puts a delay between the iteration of each element of the array and it cycles forever until the callback function returns false
:
functioniterateArrayWithDelay(arr, delay, fn) {
var index = 0;
functionnext() {
// protect against empty arrayif (!arr.length) {
return;
}
// see if we need to wrap the indexif (index >= arr.length) {
index = 0;
}
// call the callbackif (fn(arr[index], arr, index) === false) {
// stop iteratingreturn;
}
++index;
// schedule next iterationsetTimeout(next, delay);
}
// start the iterationnext();
}
And, for your example, you would use it like this:
iterateArrayWithDelay(s, 1000, myFunction);
Where you define myFunction
to be a callback function that processes each element. The callback is passed three items:
myFunction(item, array, i){
// your code here to process item
}
.delay()
only works with jQuery methods that use the animation queue. In your code example, the .delay('1000')
isn't doing anything since there are no jQuery animation methods after it on the same object.
As for memory leaks, it's hard to follow the overall context of what you're doing because we can't see the lifetime of the object represented by this
and its properties. This sequence looks pretty odd:
var x = t.s[i];
...
delete t.s[i];
t.s.push(x);
In particular, I don't see how the delete
statement is actually doing anything because you still have a reference to the contents in x
so nothing will be garbage collected. Further, delete
in javascript is used the get rid of an object property, not for freeing up an object or for removing an array element. To free up an object, you have to get rid of all references to that object (setting them to some other value so they no longer contain the reference or letting them go out of scope). Thus, since you're never getting rid of the reference to whatever is in t.s[i]
, nothing is getting freed.
Your use of setTimeout()
is not causing recursion. When you call setTimeout()
, it sets a timer and puts a function reference into a data structure that is associated with that timer. Then, the calling function continues to run and finishes it's execution. Thus, it's done executing before the setTimeout() fires and calls it again. So, it's not actually recursion. It's a bunch of sequential function calls, separated by a time interval and one finishes before the next can run (because javascript is single threaded and because the timer is set for the future).
Solution 2:
Okay, I don't use jquery and in all probability I have no clue as to what you're trying to achieve. However from what I understand I think you should do something like this:
var i = 0;
var t = this;
var timer = newDeltaTimer(function (time) {
// your animationvar x = t.s[i];
x.delay("1000").css("background-color", "#FAAF16");
delete t.s[i];
t.s.push(x);
// increment i
i++;
}, 1000);
var start = timer.start();
You will notice here that I've used a constructor called DeltaTimer
. This constructor is defined in this gist. It allows you to control your animations precisely using start
and stop
functions. The render
function which is passed is given a time
argument which is a Date
. The expression time - start
gives the exact time when the function was called (e.g. 4
, 1000
, 2000
, ...).
The advantage of using DeltaTimer
over setTimeout
or setInterval
is:
- It corrects itself. This means that animations are smoother and there's less lag.
- The animation can be controlled by starting and stopping the timer.
- The exact time of the function call is passed to the function. This helps in keeping track of which frame is being rendered, where should the sprite be rendered, etc.
- The logic for animation is separated from the logic of timing control. Thus code is more cohesive and more loosely coupled.
You can read my other answers regarding delta timing here, here and here.
Edit 1: That's actually pretty simple. We simply shift out the first element of the array, process it and then push it back at the end. Here's the logic:
functionloopIterate(array, callback, interval) {
var timer = new DeltaTimer(function (time) {
var element = array.shift();
callback(element, time - start);
array.push(element);
}, interval);
var start = timer.start();
};
Now we can create an array and loop through it as follows:
var body = document.body;
loopIterate([1, 2, 3], function (element, time) {
body.innerHTML += element + ": " + time + "<br/>";
}, 1000);
You can see the output here: http://jsfiddle.net/aGQfr/
Edit 2: Oops, I found a problem. From what I understand you want to process the next element a certain amount of time after the current element is finished processing. My delta timing script doesn't do that. It only executes functions at fixed intervals of time.
So, you don't need delta timing at all. You need to call setTimeout
after each element has been processed:
functionloopIterate(array, callback, interval) {
var start = + newDate;
process();
functionprocess() {
var element = array.shift();
callback(element, newDate - start);
array.push(element);
setTimeout(process, interval);
}
};
After that just create an array and loop through it as follows:
loopIterate([1, 2, 3], function (element, time) {
alert(element);
}, 1000);
You can see the demo here (note that your browser may not like it): http://jsfiddle.net/aGQfr/1/
Edit 3: You may also combined methods one and two so that you have a script which:
- Waits for the processing to finish before adding the next element to process to the event queue.
- Can be controlled using a
start
andstop
function. - Gives the exact time the callback is called.
- Separates processing from timing control.
We'll create a constructor function called LoopIterator
which returns an iterator object with start
and stop
methods:
functionLoopIterator(array, callback, interval) {
var start, iterate, timeout;
this.start = function () {
if (!iterate) {
start = + newDate;
iterate = true;
loop();
}
};
this.stop = function () {
if (iterate) {
clearTimeout(timeout);
iterate = false;
}
};
functionloop() {
var element = array.shift();
callback(element, newDate - start);
array.push(element);
if (iterate) timeout = setTimeout(loop, interval);
}
}
Now we can create and start a new iterator as follows:
variterator = new LoopIterator([1, 2, 3], function (element, time) {
alert(element);
}, 3000);
iterator.start();
If you wish you may even stop and start the iterator when the mouse moves over an element or out of an element respectively:
var div = document.getElementsByTagName("div")[0];
div.addEventListener("mouseout", iterator.start, false);
div.addEventListener("mouseover", iterator.stop, false);
When stopped the iterator's state is preserved and when started again it continues from where it left off.
You may see the demo here: http://jsfiddle.net/PEcUG/
Edit 4: So you want to create a simple slider? Let's start with the HTML, then the CSS and then the JavaScript.
The HTML:
<divclass="slider"><divclass="slide">Slide 1</div><divclass="slide">Slide 2</div><divclass="slide">Slide 3</div></div>
We have a div
element with a class called slider
(because there may be more than one slider on the page). Each slider has zero or more div
elements with the class slide
. Each slide may have arbitrary content. The slider will also have buttons, but we don't include this in the HTML as it will be generated automatically by JavaScript. No redundancy. Also note that none of the slides are manually numbered. Everything is handled by JavaScript.
The CSS:
.slide {
background-color: #EEEEEE;
-moz-border-radius: 0.25em;
-webkit-border-radius: 0.25em;
border-radius: 0.25em;
display: none;
padding: 1em;
}
.slider-button {
background-color: #CCCCCC;
-moz-border-radius: 0.25em;
-webkit-border-radius: 0.25em;
border-radius: 0.25em;
cursor: pointer;
float: right;
height: 1.25em;
margin: 0.5em;
width: 1.25em;
}
You may supply arbitrary CSS to suit your taste. An important point however is that .slide
must have display: none;
because the slides must initially be hidden. Also .slider-button
must have float: right;
. This is important as elements floated to the right have their order reversed. Thus the first button is actually the last button. This must be handled correctly by JavaScript so don't change it unless you know what you're doing.
The JavaScript:
Alright, I'll explain this bottom up:
window.addEventListener("DOMContentLoaded", function () {
var sliders = document.querySelectorAll(".slider");
var length = sliders.length;
for (var i = 0; i < length; i++)
newSlider(sliders[i], 2000);
}, false);
Here Slider
is a constructor function which initializes and starts the slider element it passed. It accepts the time interval between two slides as the second argument. Here's the code for Slider
:
functionSlider(slider, interval) {
var slides = slider.querySelectorAll(".slide");
var iterate, start, timeout, delay = interval;
slides = Array.prototype.slice.call(slides);
var buttons = [], numbers = [], goto = [];
var length = slides.length;
for (var i = 0; i < length; i++) {
var button = document.createElement("div");
button.setAttribute("class", "slider-button");
slider.appendChild(button);
buttons.unshift(button);
numbers.push(i + 1);
var handler = getHandler(length - i);
button.addEventListener("click", handler, false);
goto.unshift(handler);
}
this.goto = function (index) {
var gotoSlide = goto[index];
if (typeof gotoSlide === "function")
gotoSlide();
};
slider.addEventListener("mouseover", stop, false);
slider.addEventListener("mouseout", start, false);
this.start = start;
this.stop = stop;
showSlide();
start();
functionstart() {
if (!iterate) {
iterate = true;
start = + newDate;
timeout = setTimeout(loop, delay);
}
}
functionstop() {
if (iterate) {
iterate = false;
clearTimeout(timeout);
delay = interval - newDate + start;
}
}
functionloop() {
hideSlide();
slideSlider();
showSlide();
if (iterate) {
start = + newDate;
timeout = setTimeout(loop, interval);
}
}
functionhideSlide() {
slides[0].style.display = "none";
buttons[0].style.backgroundColor = "#CCCCCC";
}
functionslideSlider() {
slides.push(slides.shift());
buttons.push(buttons.shift());
numbers.push(numbers.shift());
}
functionshowSlide() {
slides[0].style.display = "block";
buttons[0].style.backgroundColor = "#FAAF16";
}
functiongetHandler(number) {
returnfunction () {
hideSlide();
while (numbers[0] !== number) slideSlider();
showSlide();
};
}
}
The code is pretty self-explanatory. Every instance of Slider
has a start
, stop
and goto
method for finer control. The goto
method takes a slide index number. For n
slides the indices range from 0
to n - 1
. That's it.
The demo of the slider is here: http://jsfiddle.net/SyTFZ/4/
Post a Comment for "Javascript Delay Between Function Calls For Each Index In An Array"