提问人:olidev 提问时间:8/27/2010 最后编辑:halferolidev 更新时间:6/19/2023 访问量:771094
如何在 JavaScript 循环中添加延迟?
How do I add a delay in a JavaScript loop?
问:
我想在循环中添加延迟/睡眠:while
我是这样试的:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
只有第一种情况是正确的:显示后,它将等待 3 秒,然后显示,但随后会不断重复。alert('hi')
alert('hello')
alert('hello')
我想要的是 after 在 3 秒后显示,然后第二次需要等待 3 秒,依此类推。alert('hello')
alert('hi')
alert('hello')
答:
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
评论
for in
Object.keys()
setTimeout
setInterval
尝试这样的事情:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
评论
const run = (t, d) => {console.log(t); t > 1 && setTimeout(run, d, --t, d)}
另一种方法是将超时时间相乘,但请注意,这与睡眠不同。循环后的代码将立即执行,只有回调函数的执行被延迟。
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
第一个超时将设置为 ,第二个超时将设置为 ,依此类推。3000 * 1
3000 * 2
评论
start
我认为你需要这样的东西:
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
*/
评论
我用 Bluebird 和递归来做到这一点。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>
评论
setTimeout
而不是 bluebeard,从而节省了依赖关系。不过我不会在这里使用。如果添加了更多在调用中使用的逻辑,例如,为数组编制索引,则它可能具有意外值。此外,它实际上不是递归的;调用堆栈在子调用发生之前清除。它只是碰巧是相同的功能。你可以用 0 和几百万的超时/延迟来证明这一点i -= 1
i
i
这是我用来遍历数组的函数:
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) 中,你可以使用 generator 和 interval 进行延迟迭代。
生成器是 ECMAScript 6 的一个新特性,是可以 暂停并恢复。调用 genFunc 不会执行它。相反,它 返回一个所谓的 generator 对象,它让我们可以控制 genFunc 的 执行。genFunc() 最初在其开头被挂起 身体。方法 genObj.next() 继续执行 genFunc, 直到下一个产量。(探索 ES6)
代码示例:
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]);
}
自动执行版本
(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);
}
它为每次迭代声明,这意味着超时是之前的超时 + 1000。这样,传递给的正是我们想要的。i
setTimeout
评论
i*3000
setTimeout
let
i < 10
setTimeout
回调中使用它时,使用 here 才更好。你可能想在你的回答中指出这一点let
此脚本适用于大多数事情
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
x
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/
评论
$.Deferred
由于 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();
当引擎到达部件时,它会设置超时并停止异步函数
的执行。然后,当超时完成时,执行将在此时继续。这非常有用,因为您可以延迟 (1) 嵌套循环,(2) 有条件地延迟,(3) 嵌套函数:await
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)); }
虽然 NodeJS 和现代浏览器现在支持 ES7,但您可能希望使用 BabelJS 对其进行转译,以便它在任何地方运行。
评论
break;
sleep
wait
timer
以下是我如何创建一个无限循环,其延迟在特定条件下中断:
// 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 支持。在节点 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:定时循环的例子,但对于反应循环,你可以使用事件,promise 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>
评论
据我所知,该函数是异步调用的。您可以做的是将整个循环包装在异步函数中,并等待包含 setTimeout 的函数,如下所示: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!")
});
请花一些时间好好了解异步编程。
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
评论
setInterval
alert
在 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 还可以使用 // 或生成器函数来实现正确的行为。(其他答案中建议的错误行为是设置一系列 3 秒警报,无论是否“接受”或完成手头的任务)async
await
Promise()
alert()
用//: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();
您可以创建一个承诺的函数。这使您能够在没有回调和熟悉的循环控制流的情况下使用 / 编写代码。sleep
setTimeout
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 中,您可以使用来避免承诺步骤(如果旧版 Node 不支持该功能,则上述代码也同样有效):timers/promises
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.
}
嘿,我知道这篇文章很旧,但是这段代码会“循环”并使用递归方法为其添加延迟。我不认为你可以“实际上”根据阅读其他人的各种评论来延迟循环本身的迭代。也许这可以帮助某人!基本上,该函数接受一个数组(在本例中)。在每次迭代中,都会调用 Javascript 方法。当函数的计时器到期时,函数会无限期地再次调用自身,但在每次调用时,数组都会变小,直到达到基本情况。我希望这可以帮助其他人。setTimeout
setTimeout
评论
setTimeout
非常简单的单行解决方案,具有实际的异步等待延迟(没有排队的 setTimeout):
await new Promise((resolve) => {setTimeout(() => {resolve(true)}, 190)});
异步函数中的实际延迟,作为通常使用具有不同超时间隔的多个 s 的替代方法,这可能会弄乱内存。在此片段的情况下,延迟 190 毫秒。setTimeout
例:
- 在 100 个循环中的每一个循环中,它都为 a 到 。
await
new Promise
resolve
- 这仅在 190 毫秒后“允许”后才会发生。在此之前,代码被 async-await / pending 阻止。
setTimeout
Promise
async function delayTest() {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 190)});
}
};
delayTest()
一个简单但不是很优雅的解决方案是嵌套第二个循环,该循环将充当计时器(本质上,嵌套循环将保持线程)。如果你正在写一些临时的东西,这是一个肮脏的解决方法。
for(i=0;i<arr.length;i++){
//you logic here
//this will act as a timer
for(j=0;j<25000;j++) console.log("")
}
上周我不得不从一个网站下载大约 40 个文件,所以我写了一个简短的 JS 脚本,通过 Chrome 的控制台执行,嵌套循环技巧在延迟执行方面做得很好。
评论