Javascript:延迟循环遍历数组

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30865456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-28 12:50:09  来源:igfitidea点击:

Javascript: Loop through Array with Delay

javascriptarrays

提问by LoveAndHappiness

I am trying to loop through an array, but want to output each value of the array with a delay. This is what my current understanding is on how it should work:

我试图遍历一个数组,但想延迟输出数组的每个值。这是我目前对它应该如何工作的理解:

EDIT

编辑

Requested JS Fiddle: http://jsfiddle.net/d3whkjww/

请求 JS 小提琴:http: //jsfiddle.net/d3whkjww/

    loopThroughSplittedText: function(splittedText) {

        for (var i = 0; i < splittedText.length; i++) {
            // for each iteration console.log a word
            // and make a pause after it
            setTimeout(
                console.log(splittedText[i]),
                1000
            );
        };

    },

Yet, it does not work, and I believe it might be, because the arguments in the "for" loop have to be inside the setTimeout function. Yet I don't know how to make it work.

然而,它不起作用,我相信它可能会起作用,因为“for”循环中的参数必须在 setTimeout 函数内。但我不知道如何使它工作。

All I get is every value of the array at once, but I want them appear with a delay. How do I do that?

我得到的只是数组的每个值,但我希望它们延迟出现。我怎么做?

采纳答案by YaBCK

In my example, it will show you how to loop through an array contentiously until you stop. This is to just give you an idea on how you can do the delay. Also it shows you when the value actually got displayed.

在我的示例中,它将向您展示如何有争议地循环遍历数组,直到您停止为止。这只是让您了解如何进行延迟。它还会向您显示实际显示值的时间。

I would say that you could actually create a nice utility from this timer, and use it for multiple purposes and with the utility it'll stop you from repeating large chunks of code.

我想说的是,您实际上可以从这个计时器创建一个不错的实用程序,并将其用于多种用途,并且该实用程序将阻止您重复大量代码。

JavaScript Loop example:

JavaScript 循环示例:

var body = document.body;
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];

loopThroughArray(splittedText, function (arrayElement, loopTime) {
    body.innerHTML += arrayElement+ ": " + loopTime+ "<br/>";
}, 1000);

function loopThroughArray(array, callback, interval) {
    var newLoopTimer = new LoopTimer(function (time) {
        var element = array.shift();
        callback(element, time - start);
        array.push(element);
    }, interval);

    var start = newLoopTimer.start();
};

// Timer 
function LoopTimer(render, interval) {
    var timeout;
    var lastTime;

    this.start = startLoop;
    this.stop = stopLoop;

    // Start Loop
    function startLoop() {
        timeout = setTimeout(createLoop, 0);
        lastTime = Date.now();
        return lastTime;
    }
    
    // Stop Loop
    function stopLoop() {
        clearTimeout(timeout);
        return lastTime;
    }
    
    // The actual loop
    function createLoop() {
        var thisTime = Date.now();
        var loopTime = thisTime - lastTime;
        var delay = Math.max(interval - loopTime, 0);
        timeout = setTimeout(createLoop, delay);
        lastTime = thisTime + delay;
        render(thisTime);
    }
}

回答by Jashwant

var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];

function loopThroughSplittedText(splittedText) {
    for (var i = 0; i < splittedText.length; i++) {
        // for each iteration console.log a word
        // and make a pause after it
        (function (i) {
            setTimeout(function () {
                document.getElementById('text').innerHTML += splittedText[i];
                console.log(splittedText[i]);
            }, 1000 * i);
        })(i);
    };
}
loopThroughSplittedText(splittedText);

Fiddle Demo

小提琴演示

回答by Curt

A recursive function call would do the job:

递归函数调用可以完成这项工作:

var a = [
    1,2,3,4,5,6,7,8,9,10
    ];

function log(i){
    console.log(a[i]);
    if (i<a.length){
       setTimeout(function(){
           i++;
           log(i);
       },1000);
    }
}

log(0);

http://jsfiddle.net/Curt/rjve4whe/1/

http://jsfiddle.net/Curt/rjve4whe/1/

回答by cloudfeet

One problem with your code is that iis common to all the callbacks. So the first callback is told "output the entry at index i", however by the time it gets to execute the initial loop is finished so iis now at the end of the text.

您的代码的一个问题i是所有回调都存在这一问题。所以第一个回调被告知“在索引处输出条目i”,但是当它开始执行时,初始循环已经完成,所以i现在是在文本的末尾。



One way to achieve what you're looking for is to not use a forloop, but to have a function which (1) prints a character, (2) updates the counter/position, and (3) schedules the next character if needed:

实现您要查找的内容的一种方法是不使用for循环,而是使用一个函数,该函数 (1) 打印一个字符,(2) 更新计数器/位置,以及 (3) 在需要时安排下一个字符:

loopThroughSplitText: function (text) {
    var i = 0;
    function printEntry() {
        console.log(text[i]);
        i++; // Increment the position
        if (i < text.length) { // If there are more chars, schedule another
            setTimeout(printEntry, 1000);
        }
    }
    printEntry(); // Print the first entry/char
}

回答by Arun P Johny

Ok, as It is not an exact duplicate, you need to increate the delay in the loop, also escape from the closure variable in a loop issue

好的,因为它不是完全重复的,所以您需要在循环中创建延迟,还需要从循环问题中的 c losure 变量中逃脱

loopThroughSplittedText: function (splittedText) {
    splittedText.forEach(function (text, i) {
        setTimeout(function () {
            console.log(text);
        }, i * 1000)
    })
}

var obj = {
  loopThroughSplittedText: function(splittedText) {
    splittedText.forEach(function(text, i) {
      setTimeout(function() {
        document.getElementById('x').innerHTML += text
      }, i * 1000)
    })
  }
}

obj.loopThroughSplittedText('abcde'.split(''))
<div id="x"></div>

回答by DracoAdvigilat

Chances are you're going to want to use a recursive function instead of a for loop here. However, I'll explain both ways just in case you (or someone else reading this) has your heart set on doing this with a loop.

很有可能你会想要在这里使用递归函数而不是 for 循环。但是,我将解释两种方式,以防万一您(或其他阅读本文的人)决心使用循环来执行此操作。

For a recursive function, the general idea is that you'll want to call the function once, then let it call itself repeatedly until it's finished doing what you want it to do. In terms of code, it will could look something a bit like this:

对于递归函数,一般的想法是您希望调用该函数一次,然后让它重复调用自己,直到它完成您想要它做的事情。在代码方面,它可能看起来有点像这样:

loopThroughSplittedText: function(splittedText) {

  // Create our counter; delayedOutput will use this to
  // track how far along in our string we are currently at
  var locationInString = 0;

  function delayedOutput() {

    // Output the next letter in our string
    console.log(splittedText[locationInString]);

    // Increment our counter so that on the next call we are on the next letter
    locationInString++;

    // Only perform setTimeout if we still have text left to output
    if (locationInString < splittedText.length) {

      // Functions can reference themselves using their own name
      setTimeout(delayedOutput, 1000);
    }
  }

  // Call our function once to get things started
  delayedOutput(); 
},

Alternatively, if you really prefer using a loop, you can still do it, but there's a fair bit of fiddling that has to be done to accomplish this.

或者,如果您真的更喜欢使用循环,您仍然可以这样做,但是要完成此操作必须进行一些操作。

First, you're going to need to place console.logwithin its own function. This is because when you place console.log(something), you're not actually passing it, but callingit right then and there, which is not what you want; by calling it, it spits out the text to the console right away rather than waiting until later. Tucking it away in its own function allows it to be passed to setTimeout so it can be called later on.

首先,您将需要放置console.log在它自己的函数中。这是因为当您放置时console.log(something),您实际上并没有传递它,而是立即调用它,这不是您想要的;通过调用它,它会立即将文本输出到控制台,而不是等到以后。将它隐藏在自己的函数中允许将其传递给 setTimeout 以便稍后调用。

Second, you're going to have to wrap thatfunction in yet anotherfunction to ensure that it's given the correct value of iwhen it fires. The reason is effectively this: Your intention is to tell the function "when you're ready, use what iwas when I set you up." However, what you're doing right now is effectively saying "when you're ready, look at i". Because the function doesn't check what iis until it's ready to fire, it won't know its value until long after you have performed the loop, meaning iwill be a number much higher than you want!

其次,您将不得不将该函数包装在另一个函数中,以确保它i在触发时获得正确的值。原因实际上是这样的:你的意图是告诉函数“当你准备好时,使用i我设置你时的东西”。然而,你现在所做的实际上是在说“当你准备好了,看看i”。因为该函数在i准备好触发之前不会检查是什么,所以直到您执行循环很久之后它才会知道它的值,这意味着i将是一个比您想要的高得多的数字!

As a bit of a sub-point to the above, you'll want to call that function immediately. This is known as an immediately invoked function expression. If you're not familiar with them, they're certainly worth looking up. Their uses are a bit unusual, but they're a powerful tool in the right spot.

作为上述的一个子点,您将希望立即调用该函数。这称为立即调用的函数表达式。如果你不熟悉它们,它们当然值得一看。它们的用途有点不寻常,但它们在正确的位置是一个强大的工具。

Finally, because you're setting up everything right here and now, you want to make sure the timeout for each function is a second apart; as it stands now, you're saying "do all of these one second from now", when your intention is "do all of these one second apart, starting one second from now". This fix is relatively easy; all you need to do is multiply your timeout by iso that you set up the first to go 1 second from now, the second to go 2 seconds from now, and so on.

最后,因为您此时此地正在设置所有内容,所以您希望确保每个函数的超时时间相隔一秒;就目前而言,您是在说“从现在开始做所有这些”,而您的意图是“从现在开始,间隔一秒钟做所有这些”。这个修复相对容易;你需要做的就是将你的超时时间乘以你i的第一个从现在开始 1 秒,第二个从现在开始 2 秒,依此类推。

All of that combined gives you code that looks something like this:

所有这些结合起来为您提供如下所示的代码:

loopThroughSplittedText: function(splittedText) {

  for (var i = 0; i < splittedText.length; i++) {
    setTimeout(
      (function(locationInString) {
        return function() {
          console.log(splittedText[locationInString]);
        };
      }(i)),
      (1000*i)
    );
  }

},

As for which solution is better, I would probably recommend the recursive function. The recursive version will only create one function that calls itself for every string you pass it, whereas the for loop version will create one function for every character in the string, which could get out of hand very quickly. Function creation (and object creation in general) can get expensive in JavaScript when you're working on larger projects, so it's generally best to favor solutions that avoid creating massive amounts of functions when possible.

至于哪个解决方案更好,我可能会推荐递归函数。递归版本只会为您传递的每个字符串创建一个调用自身的函数,而 for 循环版本将为字符串中的每个字符创建一个函数,这可能会很快失控。当您处理较大的项目时,JavaScript 中的函数创建(以及一般的对象创建)可能会变得昂贵,因此通常最好采用避免创建大量函数的解决方案。

But still, for sake of explanation, I wouldn't want to leave you without the for loop version; the knowledge could come in handy in other places. :)

但是,为了解释起见,我不想让您没有 for 循环版本;这些知识可以在其他地方派上用场。:)

回答by user2167582

solution using closure https://jsfiddle.net/x3azn/pan2oc9y/4/

使用闭包的解决方案 https://jsfiddle.net/x3azn/pan2oc9y/4/

function loopThroughSplittedText(splittedText) {
    var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
    for (var i = 0; i < splittedText.length; i++) {
        // for each iteration console.log a word
        // and make a pause after it
    (function(_i) {
        setTimeout(function() {
        window.document.getElementById('text').innerHTML = splittedText[_i];
        console.log(splittedText[_i]);
      }, 1000)
    }(i));

    }
}

loopThroughSplittedText()

回答by Maurice Perry

One more solution, with a setInterval:

另一种解决方案,使用 setInterval:

var i = 0;
var intv = setInterval(function() {
    if (i >= splittedText.length) {
        clearInterval(intv);
    } else {
        console.log(splittedText[i]);
        ++i;
    }
}, 1000);

回答by Jamiec

There are a couple of problems here

这里有几个问题

  1. setTimeoutshould take a function, not the resultof calling a function
  2. setTimeoutreturns immediately, so all the actions in your loop will be started at roughly the same moment, and all wait 1000ms before execting (notwithstanding the comment above however, which means they're all executed at the same moment).
  3. The value of iwill all be equal to splittedText.lengthfor each iteration due to not wrapping your loop control variable in a closure.
  1. setTimeout应该带一个函数,而不是调用函数的结果
  2. setTimeout立即返回,因此循环中的所有操作都将在大致相同的时刻开始,并且在执行之前都等待 1000 毫秒(尽管上面有注释,但这意味着它们都在同一时刻执行)。
  3. 由于没有将循环控制变量包装在闭包中,因此每次迭代的值i都将等于splittedText.length

What you need to do, is wait until the setTimeoutinstructions are executed before moving on to the next iteration of the loop.

您需要做的是等待setTimeout指令执行完毕,然后再进行下一次循环迭代。

For example:

例如:

var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];

function loopThroughSplittedText(splittedText) {
    displayValue(splittedText,0);
}

function displayValue(arr, i){
    if(i<arr.length){
        setTimeout(function(){
            document.getElementById('text').innerHTML = arr[i];
            console.log(arr[i])
            displayValue(arr,i+1);
        },1000)
    }
}

loopThroughSplittedText(splittedText)

Live example: http://jsfiddle.net/d3whkjww/1/

实例:http: //jsfiddle.net/d3whkjww/1/

回答by Mohammad Ashfaq

This will also work

这也将起作用

 function loopThroughSplittedText(splittedText) {

        for (var i=0; i < splittedText.length;i++) {
            (function(ind, text) {
                setTimeout(function(){console.log(text);}, 1000 + (1000 * ind));
        })(i, splittedText[i]);
    }

 }