令人困惑的For循环内部超时和关闭

时间:2015-03-01 03:12:47

标签: javascript html arrays for-loop

所以,我创建了一个容器div,在div中我有一个脚本动态创建113个div,类名为'fragment'。现在,一切都井然有序,我专门制作了113个div非常小,大约5X5像素。然后我把它们放在容器中,这样他们拼出一个标题。所有div都在一个名为 divs 的数组中。所以div是一个包含113个元素的数组。

现在这里的事情令人困惑,我想让这些div隐藏在浏览器的隐形区域后面,我通过给出'片段'的绝对定位来实现这一点,并将它们设置成隐藏例如:divs[45].style.left = -600';,之后,我创建了一个运行 for-loop 的脚本,并且在for循环内部我希望每个小元素在延迟后返回到它的位置。但是,我必须使用闭包,因为在for循环中插入SetTimeout()是很奇怪的。

for (i = 0; i < divs.length; i++) 
        {
            (function(j) 
             {
                setTimeout(function () 
                {
                    divs[j].style.left = divs[j].offsetLeft + 550;
                }, Math.floor(Math.random() * 1000));
            })(i); //Pass current value into self-executing anonymous function
        }   

我无法绕过上面的代码。我认为写Math.floor(Math.random() * 1000)的区域是以ms为单位指定延迟。但是,如果我输入1000,它将同时移动所有113个元素。我不明白这一点,它不应该在移动到阵列中的下一个元素之前等待1秒钟吗?顺便说一句,代码工作正常,最终发生的是元素随机移动给标题一个非常酷的效果。对我的封闭让人感到困惑,对我来说这似乎是一种奇怪的行为。

有人会如此善意地解释为什么会发生这种情况,如果我拿出Math.floor(Math.random() * 1000),为什么所有元素都会立即进入,而不是简单地让每一个元素每隔一秒移动一次...... *

修改 我的问题不是关于闭包,而是关于为什么取出Math.floor(Math.random() * 1000)并用'1000'代替它,导致所有元素一次移入,而不是一个接一个地间隔1000ms

4 个答案:

答案 0 :(得分:1)

所有的setTimeout都在(几乎)同时被实例化,并且因为它们都有1秒的延迟,所以它们都在(几乎)同时启动。

答案 1 :(得分:0)

如果要按顺序添加元素,则需要递归执行。如果您使用for循环,那么您将同时&#34;同时&#34;安排一堆计时器。他们都会在同一时间开始倒计时,他们都会尽快完成,而不会彼此关注。

以下是如何按顺序安排计时器的示例:

var things = [1, 2, 3];

(function next(array) {
    setTimeout(function () {
        var item = array[0],
            remaining = array.slice(1);
        console.log(item);
        if (remaining.length > 0) {
            next(remaining);
        }
    }, 1000);
}(things));

如果您正在进行大量异步工作,或者您希望处理与同步任务类似的异步任务,请尝试Async。它提供了一些很好的循环机制:

async.eachSeries(things, function (thing, callback) {
    setTimeout(function () {
        console.log(thing);
        callback();
    }, 1000);
}, function () {
    console.log('all done');
});

答案 2 :(得分:0)

好的,所以你想要这样的东西...... http://jsfiddle.net/92xx7u21/

Javascript:

var oDivs = document.getElementsByTagName('div');

window.doit = function(iLength, iCounter){
    setTimeout(function () {
        $(oDivs[iCounter]).width(($(oDivs[iCounter]).width() - 10) + "px");
        iCounter++;
        if (iCounter<iLength) {
            doit(iLength, iCounter);
        } else {
         alert('done');   
        }
    }, 1000);
};

doit(oDivs.length, 0);

基本上这是一个自我调用函数,你用底线开始一次,然后它会调用自己,直到它达到元素的长度..它可以被清理一点,但试图保持简单..你需要再次添加随机时间,因为我每循环保持1秒钟。

由于我没有你的任何其他代码,我用jQuery做了这个,使它成为一个简单的例子,在这种情况下也只是调整宽度。

答案 3 :(得分:-1)

鉴于以前的答案,我很确定你理解;这只是对代码的修改,可以做你想做的事情,甚至可以帮助你进一步理解:

for (i = 0; i < divs.length; i++) 
        {
            (function(j) 
             {
                setTimeout(function () 
                {
                    divs[j].style.left = divs[j].offsetLeft + 550;
                }, j * 1000); // simply multiply j by 1000
            })(i); //Pass current value into self-executing anonymous function
        }

编辑(解释):setTimout接受以毫秒为单位的延迟(1/1000秒),j0divs.length-1之间。通过将此值乘以在操作之间延迟的毫秒数,您最终会给它从上一次执行代码开始等待每次执行的总毫秒数。