可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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/
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.
All I get is every value of the array at once, but I want them appear with a delay. How do I do that?
回答1:
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:
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);
}
}
回答2:
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
回答3:
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/
回答4:
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
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>
回答5:
One problem with your code is that i
is 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 i
is now at the end of the text.
One way to achieve what you're looking for is to not use a for
loop, but to have a function which (1) prints a character, (2) updates the counter/position, and (3) schedules the next character if needed:
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
}
回答6:
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 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.log
within its own function. This is because when you place console.log(something)
, you're not actually passing it, but calling it 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.
Second, you're going to have to wrap that function in yet another function to ensure that it's given the correct value of i
when it fires. The reason is effectively this: Your intention is to tell the function "when you're ready, use what i
was 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 i
is until it's ready to fire, it won't know its value until long after you have performed the loop, meaning i
will be a number much higher than you want!
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 i
so that you set up the first to go 1 second from now, the second to go 2 seconds from now, and so on.
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.
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. :)
回答7:
One more solution, with a setInterval:
var i = 0;
var intv = setInterval(function() {
if (i >= splittedText.length) {
clearInterval(intv);
} else {
console.log(splittedText[i]);
++i;
}
}, 1000);
回答8:
There are a couple of problems here
setTimeout
should take a function, not the result of calling a function
setTimeout
returns 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).
- The value of
i
will all be equal to splittedText.length
for each iteration due to not wrapping your loop control variable in a closure.
What you need to do, is wait until the setTimeout
instructions are executed before moving on to the next iteration of the loop.
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/
回答9:
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]);
}
}
回答10:
Another sample:
var split = 'Lorem ipsum dolor'.split(' ');
var loop = function() {
console.log(split[0]);
split = split.slice(1);
if (split.length > 0) {
setTimeout(function() {
loop();
}, 1000);
}
}
loop();
回答11:
Bringing out an alternative solution to the problem, which is making use of the third argument to setTimeout
which is only supported in newer browsers:
(function (splittedText) {
for (var i = 0; i < splittedText.length; i++) {
setTimeout(
function(val) { console.log(val); },
i * 1000,
splittedText[i]
);
}
})(["Hello", "world", "!"]);
API documentation can be seen here (note the optional params).
回答12:
solution using closure
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()
回答13:
You can achieve by 3 ways
1. closure
2. Recursive
3. variable declaration using let
var data = ['a', 'b', 'c', 'd'];
closure:
for(i=0; i<=data.length; i++) {
(function(x) {
setTimeout(() => {
console.log(x);
}, 1000)
})(data[i]);
}
let variable declaration
for(const ind of data) {
let local = ind;
setTimeout(() => {
console.log(local);
}, 1000)
}