Javascript 将For循环与内部超时和闭包混淆

Javascript 将For循环与内部超时和闭包混淆,javascript,html,arrays,for-loop,Javascript,Html,Arrays,For Loop,因此,我创建了一个容器div,在该div中,我有一个脚本动态创建了113个div,类名为“fragment”。现在,一切都井然有序,我特意将113个div制作得非常小,大约5X5像素。然后我把它们放在容器中,这样它们就能拼出一个标题。所有div都位于一个名为div的数组中。因此divs是一个包含113个元素的数组 现在这里是让人困惑的地方,我想让那些div隐藏在浏览器的不可见区域后面,我通过给“片段”绝对位置并设置隐藏样式来实现这一点,例如:divs[45]。style.left=-600';,

因此,我创建了一个容器div,在该div中,我有一个脚本动态创建了113个div,类名为“fragment”。现在,一切都井然有序,我特意将113个div制作得非常小,大约5X5像素。然后我把它们放在容器中,这样它们就能拼出一个标题。所有div都位于一个名为div的数组中。因此divs是一个包含113个元素的数组

现在这里是让人困惑的地方,我想让那些div隐藏在浏览器的不可见区域后面,我通过给“片段”绝对位置并设置隐藏样式来实现这一点,例如:divs[45]。style.left=-600';,在那之后,我创建了一个脚本,它将运行for循环,在for循环中,我希望每个小元素在延迟后返回到它的位置。然而,我不得不为此使用闭包,因为插入for循环的setTimeOutside很奇怪

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.floorMath.random*1000的区域是以毫秒为单位指定延迟。但是,如果输入1000,它将一次移动所有113个元素。我不明白这一点,它是否应该在移动到数组中的下一个元素之前等待1秒?顺便说一句,代码运行得很好,最终发生的是元素随机移动,给标题带来了非常酷的效果。闭包对我来说已经够让人困惑的了,对我来说,它看起来真的很奇怪

请有人解释一下为什么会发生这种情况,如果我去掉Math.floorMath.random*1000,为什么所有元素都会同时移动,而不是简单地每秒钟移动一个元素*

编辑
我的问题不是关于闭包,而是为什么去掉Math.floorMath.random*1000并用“1000”替换它会导致所有元素同时移入,而不是以1000毫秒的间隔逐个移入。

所有setTimeout都是在几乎相同的时间实例化的,因为它们都有1秒的延迟,它们几乎同时启动。

如果要按顺序添加元素,则需要递归执行。如果您使用for循环,那么您将同时调度一组计时器。他们将同时开始倒数,并且他们将尽快完成,而不考虑彼此

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

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.eachSeries(things, function (thing, callback) {
    setTimeout(function () {
        console.log(thing);
        callback();
    }, 1000);
}, function () {
    console.log('all done');
});

我很确定你已经理解了前面的答案;这只是对代码的修改,以实现您的目的,甚至可以帮助您进一步理解:

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秒,并且j介于0和divs.length-1之间。通过将该值乘以两个操作之间的延迟毫秒数,可以得到从执行上述代码开始等待每次执行的总毫秒数

好吧,那么你想要这样的东西

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进行了此操作,以使其成为一个简单的示例,并在本例中调整了宽度。

您需要在此处更改方法,使用一个函数调用自身,并使用setTimeout。。。这样它会打一次电话,然后再等。。再打一次,然后等待,等等。您还可以传递一两个参数,例如divs.length和var i;Jon已经回答了你的问题,所有的超时都会一个接一个地触发,没有延迟,因为for循环不会等待每个超时,…哦,好吧,你介意再详细解释一下吗?我认为for循环直到到达当前循环的末尾才结束。while循环会改变这一点吗?for循环会继续。它不知道您正在它的主体中进行异步工作。for循环确实结束了。setTimeout调用异步执行。while循环也没什么不同。JS触发setTimeout并将其放入队列中,但没有任何地方表明JS将等待超时触发-这是挂起系统的一种可靠方法。你可以使用setTimeout调用setTimeout谢谢,Jon告诉我同样的事情,但我有点慢,不明白为什么。据我所知,for循环直到它内部的所有内容都被执行后才重新开始。所以我认为一秒钟的延迟会停止下一次迭代的执行。它一直在继续。for循环对于同步编程比async.Yo更有用
你不需要递归地这样做。。。我的答案是对OP的代码进行简单的修改,效果非常好。对于长时间运行的应用程序,您不能保证时间间隔。无论您在何处使用setTimeout,都会受到CPU负载导致的计时器不准确的影响。等待最后一个计时器完成后再启动下一个计时器只能保证您至少要等待指定的时间,这意味着运行时间较长的应用程序执行的平均时间可能要比实际时间长得多,而当同时启动所有超时时,执行之间的平均延迟将更加准确。对不起,我删除了我的上一条评论,因为我的提琴不够有说服力:这里有一个更好的递归评论:还有一个迭代评论:有人能详细说明为什么这被否决吗?我真的很喜欢你的解决方案,它非常聪明。顺便说一句,我没有投你反对票,但我猜是有人投了反对票,因为你没有提到其他两个答案。如果可以的话,我会投你一票,因为你通过改变一行就解决了“潜在”问题。美好的好的,谢谢你的反馈。我很高兴,只要有人知道答案: