函数调用之间的javascript延迟数组中的每个索引

Dan*_*nze 1 javascript jquery

目标:

  • 对数组中的每个元素执行逻辑.
  • 等待X下一次执行之间的ms.
  • mouseover(#slider)暂停延迟 - 如果延迟= 1000毫秒,并且已经过了300毫秒,mouseout(#slider)将触发恢复倒计时剩余的700毫秒延迟.
  • 在最后一个元素上执行后,循环返回再次执行 - 永远.

这是一个直观的解释:

var = s Array(1,2,3)

var x = s[1];   //get first element   
console.log(x); //do something to it
wait();         //START wait timer 1000ms

//------------> timer : 300ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : waited 5000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 300ms --> still 700ms to go!
//------------> timer : 500ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : waited 10000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 500ms --> still 500ms to go!

var x = s[2];   //get second element   
console.log(x); //do something to it
wait();         //START wait timer 1000ms

//------------> timer : 200ms
//------------> user  : mouseover (#slider) : pause timer
//------------> user  : onclick   (.slideButton1) : change index of array and clear timer
//------------> user  : waited 6000ms
//------------> user  : mouseout  (#slider) : resume timer
//------------> timer : 0ms --> still 1000ms to go!

var x = s[1];   //get first element   ( index was changed by clicking button )
console.log(x); //do something to it
wait();         //START wait timer 1000ms

// ... s[2] ... s[3] ...
//theres nothing else in the array, lets start back from s[1] again!
Run Code Online (Sandbox Code Playgroud)

解决方案:

jQuery的:

http://jsfiddle.net/zGd8a/8/

这个解决方案来自一个相关的帖子.这个插件的官方来源可以在这里找到.

原生JS:

http://jsfiddle.net/SyTFZ/4/

答案Aadit中号沙阿是真的很有帮助.他还详细介绍了Delta Timing以及它在类似情况下的用途.

新目标:

摘要这两种方法中的任何一种都可以用于其他事情.

jfr*_*d00 6

好了,既然你已经完全简化了问题,这里是一个通用的数组迭代器函数,它在数组的每个元素的迭代之间放置一个延迟,并且它会一直循环直到回调函数返回false:

function iterateArrayWithDelay(arr, delay, fn) {
    var index = 0;

    function next() {
        // protect against empty array
        if (!arr.length) {
            return;
        }

        // see if we need to wrap the index
        if (index >= arr.length) {
            index = 0;
        }

        // call the callback
        if (fn(arr[index], arr, index) === false) {
            // stop iterating
            return;
        }

        ++index;

        // schedule next iteration
        setTimeout(next, delay);
    }
    // start the iteration
    next();
}
Run Code Online (Sandbox Code Playgroud)

而且,对于您的示例,您可以像这样使用它:

iterateArrayWithDelay(s, 1000, myFunction);
Run Code Online (Sandbox Code Playgroud)

您定义myFunction为处理每个元素的回调函数的位置.回调传递了三个项目:

myFunction(item, array, i){
    // your code here to process item
}
Run Code Online (Sandbox Code Playgroud)

.delay()仅适用于使用动画队列的jQuery方法.在你的代码示例中,.delay('1000')由于在同一对象之后没有jQuery动画方法,所以没有做任何事情.

至于内存泄漏,很难跟踪你正在做的事情的整体上下文,因为我们无法看到所代表的对象的生命周期this及其属性.这个序列看起来很奇怪:

var x = t.s[i];
...
delete t.s[i];
t.s.push(x);
Run Code Online (Sandbox Code Playgroud)

特别是,我没有看到delete语句实际上是如何做的,因为你仍然有对内容的引用,x所以什么都不会被垃圾收集.此外,delete在javascript中使用了摆脱对象属性,而不是用于释放对象或删除数组元素.要释放对象,您必须删除对该对象的所有引用(将它们设置为其他值,以便它们不再包含引用或让它们超出范围).因此,既然你永远不会摆脱对任何东西的引用,那么什么都t.s[i]不会被释放.


您的使用setTimeout()不会导致递归.当您调用时setTimeout(),它会设置一个计时器并将函数引用放入与该计时器关联的数据结构中.然后,调用函数继续运行并完成它的执行.因此,它在setTimeout()触发之前完成执行并再次调用它.所以,它实际上不是递归.它是一堆顺序函数调用,以时间间隔分隔,一个在下一个可以运行之前完成(因为javascript是单线程的,因为计时器是为将来设置的).


Aad*_*hah 5

好吧,我不使用 jquery,很可能我不知道你想要实现什么。然而,根据我的理解,我认为你应该这样做:

var i = 0;
var t = this;

var timer = new DeltaTimer(function (time) {
    // your animation
    var 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();
Run Code Online (Sandbox Code Playgroud)

您会注意到这里我使用了一个名为 的构造函数DeltaTimer此构造函数在此gist中定义。start它允许您使用和功能精确控制动画stoprender传递的函数被赋予一个参数time,该参数是Date。该表达式time - start给出了调用函数的确切时间(例如41000, 2000, ...)。

DeltaTimer使用setTimeoutor的优点setInterval是:

  1. 会自我纠正。这意味着动画更流畅并且延迟更少。
  2. 可以通过启动和停止计时器来控制动画。
  3. 函数调用的确切时间被传递给函数。这有助于跟踪正在渲染哪个帧、应该在哪里渲染精灵等。
  4. 动画的逻辑与时序控制的逻辑是分离的。因此,代码更加内聚并且更加松散耦合。

您可以在这里这里这里阅读我关于增量计时的其他答案。

编辑1:这实际上非常简单。我们只需移出数组的第一个元素,对其进行处理,然后将其推回末尾。逻辑如下:

function loopIterate(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();
};
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建一个数组并循环遍历它,如下所示:

var body = document.body;

loopIterate([1, 2, 3], function (element, time) {
    body.innerHTML += element + ": " + time + "<br/>";
}, 1000);
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看输出:http://jsfiddle.net/aGQfr/

编辑2:哎呀,我发现了一个问题。据我了解,您希望在当前元素完成处理一定时间处理下一个元素。我的增量计时脚本不会这样做。它仅以固定的时间间隔执行功能。

因此,您根本不需要增量计时。您需要setTimeout在处理每个元素后调用:

function loopIterate(array, callback, interval) {
    var start = + new Date;
    process();

    function process() {
        var element = array.shift();
        callback(element, new Date - start);
        array.push(element);

        setTimeout(process, interval);
    }
};
Run Code Online (Sandbox Code Playgroud)

之后,只需创建一个数组并循环遍历它,如下所示:

loopIterate([1, 2, 3], function (element, time) {
    alert(element);
}, 1000);
Run Code Online (Sandbox Code Playgroud)

您可以在此处查看演示(请注意,您的浏览器可能不喜欢它):http://jsfiddle.net/aGQfr/1/

编辑 3:您还可以组合方法一和方法二,以便您拥有一个脚本:

  1. 等待处理完成,然后再将下一个要处理的元素添加到事件队列。
  2. 可以使用startandstop函数进行控制。
  3. 给出调用回调的确切时间。
  4. 将处理与时序控制分开。

我们将创建一个名为 的构造函数LoopIterator,它返回一个具有startstop方法的迭代器对象:

function LoopIterator(array, callback, interval) {
    var start, iterate, timeout;

    this.start = function () {
        if (!iterate) {
            start = + new Date;
            iterate = true;
            loop();
        }
    };

    this.stop = function () {
        if (iterate) {
            clearTimeout(timeout);
            iterate = false;
        }
    };

    function loop() {
        var element = array.shift();
        callback(element, new Date - start);
        array.push(element);

        if (iterate) timeout = setTimeout(loop, interval);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以创建并启动一个新的迭代器,如下所示:

var iterator = new LoopIterator([1, 2, 3], function (element, time) {
    alert(element);
}, 3000);

iterator.start();
Run Code Online (Sandbox Code Playgroud)

如果您愿意,甚至可以在鼠标移到元素上或移出元素时分别停止和启动迭代器:

var div = document.getElementsByTagName("div")[0];
div.addEventListener("mouseout", iterator.start, false);
div.addEventListener("mouseover", iterator.stop, false);
Run Code Online (Sandbox Code Playgroud)

当停止时,迭代器的状态被保留,当再次启动时,它从停止的地方继续。

您可以在这里看到演示:http://jsfiddle.net/PEcUG/

编辑 4:那么您想创建一个简单的滑块吗?让我们从 HTML 开始,然后是 CSS,最后是 JavaScript。

HTML:

<div class="slider">
    <div class="slide">Slide 1</div>
    <div class="slide">Slide 2</div>
    <div class="slide">Slide 3</div>
</div>
Run Code Online (Sandbox Code Playgroud)

我们有一个div元素,其类名为slider(因为页面上可能有多个滑块)。每个滑块都有零个或多个div属于 class 的元素slide。每张幻灯片可以有任意内容。滑块还将有按钮,但我们不会将其包含在 HTML 中,因为它将由 JavaScript 自动生成。没有冗余。另请注意,所有幻灯片都没有手动编号。一切都由 JavaScript 处理。

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;
}
Run Code Online (Sandbox Code Playgroud)

您可以提供任意 CSS 以满足您的口味。然而,重要的一点是.slide必须具有display: none;,因为幻灯片最初必须隐藏。也.slider-button必须有float: right;。这很重要,因为向右浮动的元素的顺序是相反的。因此第一个按钮实际上是最后一个按钮。这必须由 JavaScript 正确处理,因此除非您知道自己在做什么,否则不要更改它。

JavaScript:

好吧,我会从下往上解释一下:

window.addEventListener("DOMContentLoaded", function () {
    var sliders = document.querySelectorAll(".slider");
    var length = sliders.length;

    for (var i = 0; i < length; i++)
        new Slider(sliders[i], 2000);
}, false);
Run Code Online (Sandbox Code Playgroud)

Slider是一个构造函数,它初始化并启动它传递的滑块元素。它接受两张幻灯片之间的时间间隔作为第二个参数。这是代码Slider

function Slider(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();

    function start() {
        if (!iterate) {
            iterate = true;
            start = + new Date;
            timeout = setTimeout(loop, delay);
        }
    }

    function stop() {
        if (iterate) {
            iterate = false;
            clearTimeout(timeout);
            delay = interval - new Date + start;
        }
    }

    function loop() {
        hideSlide();
        slideSlider();
        showSlide();

        if (iterate) {
            start = + new Date;
            timeout = setTimeout(loop, interval);
        }
    }

    function hideSlide() {
        slides[0].style.display = "none";
        buttons[0].style.backgroundColor = "#CCCCCC";
    }

    function slideSlider() {
        slides.push(slides.shift());
        buttons.push(buttons.shift());
        numbers.push(numbers.shift());
    }

    function showSlide() {
        slides[0].style.display = "block";
        buttons[0].style.backgroundColor = "#FAAF16";
    }

    function getHandler(number) {
        return function () {
            hideSlide();
            while (numbers[0] !== number) slideSlider();
            showSlide();
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

该代码非常不言自明。的每个实例Slider都有一个start,stopgoto方法以进行更精细的控制。该goto方法采用幻灯片索引号。对于n幻灯片,索引范围为0n - 1。就是这样。

滑块的演示在这里: http: //jsfiddle.net/SyTFZ/4/