如何在JavaScript循环中添加延迟?

我想在while循环中添加一个delay/sleep:

我是这样试的:

alert('hi');


for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}

只有第一种情况是正确的:在显示alert('hi')之后,它将等待3秒,然后alert('hello')将被显示,但随后alert('hello')将不断重复。

我想要的是,在alert('hello')显示3秒后alert('hi'),然后它需要等待3秒的第二次alert('hello')等。

695291 次浏览

setTimeout()函数是非阻塞的,会立即返回。因此,您的循环将非常快速地迭代,它将快速连续地启动一个接一个的3秒超时触发器。这就是为什么你的第一个警告会在3秒后弹出,而所有其他的警告都会紧随其后,没有任何延迟。

你可能想用这样的东西代替:

var i = 1;                  //  set your counter to 1


function myLoop() {         //  create a loop function
setTimeout(function() {   //  call a 3s setTimeout when the loop is called
console.log('hello');   //  your code here
i++;                    //  increment the counter
if (i < 10) {           //  if the counter < 10, call the loop function
myLoop();             //  ..  again which will trigger another
}                       //  ..  setTimeout()
}, 3000)
}


myLoop();                   //  start the loop

你也可以通过使用一个自调用函数,将迭代次数作为参数传递给它:

(function myLoop(i) {
setTimeout(function() {
console.log('hello'); //  your code here
if (--i) myLoop(i);   //  decrement i and call myLoop again if i > 0
}, 3000)
})(10);                   //  pass the number of iterations as an argument

试试这样做:

var i = 0, howManyTimes = 10;


function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}


f();

另一种方法是将超时时间相乘,但请注意这是不像睡觉。循环之后的代码将立即执行,只有回调函数的执行被延迟。

for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello');  }, 3000 * start);

第一个超时将被设置为3000 * 1,第二个超时将被设置为3000 * 2,以此类推。

我认为你需要这样的东西:

var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};


TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}

测试代码:

var now = +new Date();


var x = new TimedQueue(2000);


x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});


x.run();
注意:使用警报会延迟javascript的执行,直到你关闭警报。 它的代码可能比你要求的要多,但这是一个健壮的可重用解决方案

我可能会使用setInterval,像这样:

var period = 1000; // ms
var endTime = 10000;  // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
/*
Use Recursive  and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/


function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else  console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}


//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}


//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}


//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));


//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/

我这样做与蓝鸟的Promise.delay和递归。

function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}


myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>

下面是我用来循环数组的函数:

function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){


if (i < theArray.length && typeof delayAmount == 'number'){


console.log("i "+i);


theFunction(theArray[i], i);


setTimeout(function(){


loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{


onComplete(i);
}
}

你可以这样使用它:

loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}

在ES6 (ECMAScript 2015)中,您可以使用发电机和interval迭代延迟。

生成器是ECMAScript 6的一个新特性,是可以被生成的函数 顿了顿,接着说。调用genFunc不会执行它。相反,它 返回一个所谓的生成器对象,让我们控制genFunc的 执行。genFunc()最初是挂起在它的开始 的身体。方法genObj.next()继续执行genFunc, 直到下一次丰收。 (探索ES6) < / p >
< p > < br > 代码示例:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();


let val = genObj.next();
console.log(val.value);


let interval = setInterval(() => {
val = genObj.next();
  

if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);


function* genFunc() {
for(let item of arr) {
yield item;
}
}

所以如果你正在使用ES6,这是实现延迟循环的最优雅的方式(在我看来)。

我只是想在这里发表我的意见。此函数运行具有延迟的迭代循环。看到这jsfiddle。函数如下:

function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}

例如:

//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});

相当于:

//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}

.
    var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;


function functionToRun(i, length) {
alert(data[i]);
}


(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);

Daniel Vassallo回答的修改版本,将变量提取为参数,使函数更具可重用性:

首先让我们定义一些基本变量:

var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;

接下来,您应该定义要运行的函数。这将传递i,循环的当前索引和循环的长度,以防你需要它:

function functionToRun(i, length) {
alert(data[i]);
}

Self-executing版本

(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);

功能版

function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}


forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it

如果使用ES6,你可以使用for循环来实现:

for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}

它为每个迭代声明i,这意味着超时是+ 1000之前的超时。这样,传递给setTimeout的就是我们想要的。

这个脚本适用于大多数情况

function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}


for(var start = 1; start < 10; start++) {
timer(start);
}

你可以使用RxJS interval运营商intervalx秒发出一个整数,而take指定它发出这些数字的次数。

Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>

这是可行的

for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}

试试这把小提琴:https://jsfiddle.net/wgdx8zqq/

因为ES7有一个更好的方法等待循环:

// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))


async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}


load();

当引擎到达await部分时,它设置一个超时和停止async function . xml的执行。然后,当超时完成时,继续执行。这是非常有用的,因为你可以延迟(1)嵌套循环,(2)有条件的,(3)嵌套函数:

async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}


async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
    

main();


function timer(ms) { return new Promise(res => setTimeout(res, ms)); }

MDN上的引用

虽然ES7现在被NodeJS和现代浏览器支持,但你可能想要用BabelJS编译它,这样它就可以在任何地方运行。

下面是我如何创建一个无限循环的延迟,在特定条件下中断:

  // Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}

这里的关键是创建一个通过超时解析的新Promise,并等待它的解析。

显然你需要async/await支持。工作在Node 8。

对于常用的“忘记正常循环”和使用这个组合的“setInterval”包括“setTimeOut”:像这样(从我的实际任务)。

        function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}

PS:理解(setTimeOut)的真实行为:它们都将在同一时间开始“三个bla bla bla将在同一时刻开始倒数”,因此设置不同的超时来安排执行。

PS 2:计时循环的例子,但对于一个反应循环,你可以使用事件,承诺async await ..

<!DOCTYPE html>
<html>
<body>


<button onclick="myFunction()">Try it</button>


<p id="demo"></p>


<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>


</body>
</html>

试试这个…

var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}


function beginCount(item,icount){
setTimeout(function () {


new actualFunction(item,icount);


}, icount);
}


function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

简单实现了在循环运行期间每两秒显示一段文本。

for (var i = 0; i < foo.length; i++) {
setInterval(function(){
console.log("I will appear every 2 seconds");
}, 2000);
break;
};

据我所知,setTimeout函数是异步调用的。你能做的是将整个循环包装在一个async函数中,并等待包含setTimeout的Promise,如下所示:

var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}

然后像这样调用运行它:

looper().then(function(){
console.log("DONE!")
});

请花些时间好好理解异步编程。

你这样做:

console.log('hi')
let start = 1
setTimeout(function(){
let interval = setInterval(function(){
if(start == 10) clearInterval(interval)
start++
console.log('hello')
}, 3000)
}, 3000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

   let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}

在ES6中,你可以这样做:

 for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}

在ES5中,你可以这样做:

for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}

原因是,let允许你声明变量,限制在块语句的范围内,或使用它的表达式,不像var关键字,它全局定义一个变量,或局部定义一个整个函数,而不管块范围。

试试这个

 var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);

结果

A // after 2s
B // after 2s
C // after 2s

除了10年前公认的答案,使用更现代的Javascript,可以使用async/await/Promise()或生成器函数来实现正确的行为。(其他答案中建议的错误行为是设置一系列3秒警报,而不管“接受”;alert() -或完成手头的任务)

< p >使用async / await / Promise():
alert('hi');


(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();

使用生成器函数:

alert('hi');


let func;


(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();

你可以创建一个许诺setTimeoutsleep函数。这使得你可以使用async/await来编写代码,而不需要回调和熟悉的for循环控制流。

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));


(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}


console.log("done");
})();

在Node中,你可以使用timers/promises来避免承诺步骤(如果旧版本的Node不支持该特性,上面的代码也可以工作):

const {setTimeout: sleep} = require("timers/promises");


// same code as above

无论如何,由于JS是单线程的,超时是异步的是一件好事。如果不这样做,浏览器就没有机会重新绘制UI,从而导致用户的界面冻结。

在我看来,在循环中添加延迟的最简单和最优雅的方法是这样的:

names = ['John', 'Ana', 'Mary'];


names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000);  // one sec interval
});

一个无功能的解决方案

我有点晚了,但有一个不使用任何函数的解决方案:

alert('hi');


for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return


// Remove the first element from the array.
const item = arr.shift()


// Set timout
setTimeout(() => {
console.log('Hello, world!', item)  // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}

嘿,我知道这篇文章很旧了,但是这段代码&;并使用递归方法添加延迟。我不认为你可以“实际上”延迟一个循环本身的迭代,基于阅读来自其他人的各种评论。也许这可以帮助一些人!基本上,函数接受一个数组(在本例中)。在每次迭代中调用setTimeout Javascript方法。当setTimeout函数的计时器到期时,该函数会再次无限地调用自己,但每次调用时,数组都会变小,直到达到基准大小写。我希望这能帮助到其他人。

非常简单的单行解决方案,具有实际的异步等待延迟(没有排队setTimeout):

下面的(自动执行匿名)函数在循环之间创建一个实际的延迟,而不是多个具有不同超时的__abc0,这可能会弄乱内存。

  • 在100个循环中的每一个循环中,它都__abc0从new Promiseresolve
  • 这只发生在setTimeout '允许'它在90ms后。在此之前,代码将被async-await / pending Promise阻塞。

(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()