499

I would like to add a delay/sleep inside a while loop:

I tried it like this:

alert('hi');

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

Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.

What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.

halfer
  • 19,824
  • 17
  • 99
  • 186
olidev
  • 20,058
  • 51
  • 133
  • 197
  • for(var i=0; i < 5; i++){delayLoop(i)}; function delayLoop(i){setTimeout(function(){console.log('printing with 1sec delay'),(i*1000)} – mhndlsz Jul 01 '21 at 05:51
  • const setTimeOutFn= async()=>{ for(var start = 0; start < 3; start++) { await new Promise( async(res , rej )=>{ setTimeout(() => { console.log('hello', start); res() }, 3000); }) } } – Bilal Khursheed Dec 29 '21 at 12:56
  • Setting a timeOut in every loop, just with a different value, might not be a good idea. Here is a boring one-liner that actually halts code execution using a promise (async/await): https://stackoverflow.com/questions/3583724/how-do-i-add-a-delay-in-a-javascript-loop/73588338#73588338 – marko-36 Dec 05 '22 at 22:41

33 Answers33

960

The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.

You may want to use something like this instead:

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

You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:

(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
double-beep
  • 5,031
  • 17
  • 33
  • 41
Daniel Vassallo
  • 337,827
  • 72
  • 505
  • 443
  • 46
    Wouldn't using recursion to implement this be subject to a stack overflow eventually? If you wanted to do a million iterations, what would be a better way to implement this? Maybe setInterval and then clear it, like Abel's solution below? – Adam Jun 24 '14 at 21:15
  • I noticed this works too if you are delaying executing functions within the loop. I tried to setTimeout a function within a for loop but it wasn't passing any of the local variables. – ericjam Sep 21 '14 at 18:55
  • Recursion is not great when you want to do a lot of loops...I am finding this is slowing down at a few 1000 iterations. – Remixed123 Mar 22 '15 at 09:01
  • 14
    @Adam: my understanding is that, since setTimeout is non-blocking, this isn't recusion - the stackwindow closes after each setTimeout and there is only ever one setTimeout waiting to execute...Right? – Joe Sep 02 '15 at 17:10
  • 4
    How would this work when iterating an object like a `for in` loop? – vsync Sep 14 '15 at 20:35
  • 1
    @vsync Look into `Object.keys()` – Braden Best Aug 12 '16 at 18:41
  • I assume the timeout will continue to exist even after i has reached 0. I think you'll want to include an else-statement with a clearTimeout, to somehow stop the setTimeout. – joey Jan 10 '17 at 11:55
  • 1
    @joey You are confusing `setTimeout` with `setInterval`. Timeouts are implicitly destroyed when the callback is called. – cdhowie Mar 01 '18 at 18:08
  • 1
    @Adam You're talking about recursion causing a stack overflow on stackoverflow.com. You win the Internets! – deltaray Feb 25 '19 at 23:53
  • You'll need to add clearInterval in order to prevent making unnecessary calls to the stack. – Aditya Mar 01 '19 at 14:40
  • may be this is more complete answer: https://stackoverflow.com/a/45500721/3131433 , it deals with the fact that you do not need to trigger a new `setTimeout()` for each loop iteration. – Rakibul Haq Jul 31 '19 at 15:14
  • const setTimeOutFn= async()=>{ for(var start = 0; start < 3; start++) { await new Promise( async(res , rej )=>{ setTimeout(() => { console.log('hello', start); res() }, 3000); }) } } – Bilal Khursheed Dec 29 '21 at 12:56
298

Since ES7 theres a better way to await a loop:

// 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();

When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:

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)); }

Reference on MDN

While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.

vsync
  • 118,978
  • 58
  • 307
  • 400
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • It works fine for me. I just want to ask that if I want to break loop , how can I do it when using await ? – Sachin Shah Aug 07 '18 at 11:25
  • 1
    @sachin `break;` maybe? – Jonas Wilms Aug 07 '18 at 11:47
  • 1
    Thanks for this solution. It is nice to use all existing control structures and not need to invent continuations. – Gus Dec 14 '19 at 22:08
  • This would still just create various timers and they would resolve at different times rather than in sequence? – David Yell May 18 '20 at 18:00
  • @JonasWilms Seems I totally missed the 'Run snippet' button :facepalm: – David Yell May 19 '20 at 10:39
  • 16
    This is by far the best solution and should be the accepted answer. The accepted answer is hacky and should not be used for anything. – Krokodil Jun 20 '21 at 15:56
  • 3
    Good solution, but to nitpick I'd call the function `sleep` or `wait` rather than `timer`. Classes are nouns, functions are verbs. They do something or take an action rather than representing a thing. – ggorlen Jul 16 '22 at 22:06
123

If using ES6, you could use a for loop to achieve this:

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

It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.

adiga
  • 34,372
  • 9
  • 61
  • 83
Saket Mehta
  • 2,438
  • 2
  • 23
  • 28
  • 3
    I believe this has the same memory allocation issues as the answer described in http://stackoverflow.com/a/3583795/1337392 – Flame_Phoenix Jul 21 '16 at 10:58
  • 2
    @Flame_Phoenix What memory allocation issues? – 4castle Jul 21 '17 at 00:29
  • 3
    The setTimeout call synchronously calculates the value of the `i*3000` argument, inside the loop, and passes it to `setTimeout` by value. Usage of `let` is optional and unrelated to the questionl and answer. – traktor Nov 21 '18 at 00:58
  • 2
    @Flame_Phoenix mentioned there are issues in this code. Basically on first pass you create timer then immediately repeat loop again and again until loop end by condition (`i < 10`) so you will have multiple timers work in parallel which create memory allocation and it's worse on larger amount of iterations. – XCanG Jul 17 '19 at 21:09
  • 1
    @traktor53 is totally right, using `let` here is only superior if you plan to use it *within the `setTimeout` callback*. You might want to point that out in your answer – Jonas Wilms Aug 12 '19 at 12:43
82

Try something like this:

var i = 0, howManyTimes = 10;

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

f();
adiga
  • 34,372
  • 9
  • 61
  • 83
cji
  • 6,635
  • 2
  • 20
  • 16
25

Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.

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

The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.

Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
  • 3
    It's worth pointing out that you cannot reliably use `start` inside your function using this method. – DBS Jun 24 '15 at 13:17
  • 2
    Bad practice - unnecessary memory allocation. – Alexander Trakhimenok Oct 07 '15 at 15:37
  • Upvote for creativity, but it's damn bad practice. :) – Salivan Jun 27 '16 at 18:51
  • 2
    Why is it a bad practice, and why does it have memory allocation issues ? Does this answer suffer the same problems? http://stackoverflow.com/a/36018502/1337392 – Flame_Phoenix Jul 21 '16 at 11:00
  • 1
    @Flame_Phoenix it's bad practice because the program will keep one timer for each loop, with all timers running at the same time. So if there are 1000 iterations, there will be 1000 timers running at the same time in the beginning. – Joakim Aug 09 '18 at 18:18
  • In addition to being bad practice, it is actually the wrong behavior and not answering the question. It does not 'wait in a loop" and not adds 3 seconds after `alert()` was cleared. It sets `alert`s in 3 seconds (plus some change) intervals. – niry Oct 24 '20 at 15:03
20

This will work

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

Try this fiddle: https://jsfiddle.net/wgdx8zqq/

Rohit Sharma
  • 3,304
  • 2
  • 19
  • 34
Gsvp Nagaraju
  • 217
  • 2
  • 4
18

I think you need something like this:

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;
    }
}

Test code:

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();

Note: using alerts stalls javascript execution till you close the alert. It might be more code than you asked for, but this is a robust reusable solution.

BGerrissen
  • 21,250
  • 3
  • 39
  • 40
18

You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.

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");
})();

In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):

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

// same code as above

Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.

ggorlen
  • 44,755
  • 7
  • 76
  • 106
Anoop
  • 11
  • 1
  • 3
16

I would probably use setInterval, like this:

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);
Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Abel Terefe
  • 1,440
  • 20
  • 17
  • 3
    SetTimeout is much better than settinterval. google it and you will know – Airy Feb 23 '14 at 22:26
  • 17
    I google it around a little and I found nothing, Why setInterval is bad ? Can you give us a link ? or an example ? Thanks – Marcs Mar 20 '16 at 22:08
  • I guess [the point](https://chrisblackwell.me/setinterval-is-bad/) was that `SetInterval()` keeps spawning 'threads' even in the event of some error or block. – Mateen Ulhaq Nov 21 '17 at 01:00
13

In my opinion, the simpler and most elegant way to add a delay in a loop is like this:

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

names.forEach((name, i) => {
 setTimeout(() => {
  console.log(name);
 }, i * 1000);  // one sec interval
});
Leon Grin
  • 889
  • 8
  • 10
  • 1
    That is actually bad. forEach does not wait your setTimeout. You just create as many timeouts as there are elements in the array. For a 3 element array, it is ok. But if you have hundreds or even thousands your computer may catch fire. – Omar Omeiri Dec 05 '22 at 18:50
10

In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.

Generators, a new feature of ECMAScript 6, are functions that can be paused and resumed. Calling genFunc does not execute it. Instead, it returns a so-called generator object that lets us control genFunc’s execution. genFunc() is initially suspended at the beginning of its body. The method genObj.next() continues the execution of genFunc, until the next yield. (Exploring ES6)


Code example:

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;
  }
}

So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).

Itay Radotzki
  • 857
  • 1
  • 8
  • 11
6

Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):

await new Promise((resolve) => {setTimeout(() => {resolve(true)}, 190)});

An actual delay inside an async function, as an alternative ot commonly used multiple setTimeouts with different timeout intervals, which might mess up memory. 190ms delay in case of this snippet.

Example:

  • In each of the 100 loops, it awaits for a new Promise to resolve.
  • This happens only after setTimeout 'allows' it after 190ms. Until then, code is blocked by the async-await / pending Promise.

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

delayTest()
marko-36
  • 1,309
  • 3
  • 23
  • 38
5

I do this with Bluebird’s Promise.delay and recursion.

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>
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
Dave Bryand
  • 601
  • 6
  • 11
  • This [works fine with the native `setTimeout`](https://stackoverflow.com/a/35920536/6243352) instead of bluebeard, saving a dependency. I wouldn't use `i -= 1` here though. If more logic is addded that uses `i` in the call, for example, to index into an array, it might have an unexpected value. Also, it's not actually recursive; the call stack clears before the child call occurs. It just happens to be the same function. You can prove this with a timeout/delay of 0 and an `i` of a few million – ggorlen Jul 16 '22 at 21:44
4

In ES6 you can do as following:

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

In ES5 you can do as:

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

The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.

Tabish
  • 1,592
  • 16
  • 13
4

A function-less solution

I am a bit late to the party, but there is a solution without using any functions:

alert('hi');

for(var start = 1; start < 10; start++) {
  setTimeout(() => alert('hello'), 3000 * start);
}
Shannarra
  • 521
  • 4
  • 17
  • That will schedule 10 alerts in 3 seconds intervals, instead of 3 seconds wait time after the alert() has cleared. If the first alert() was not cleared in 30 seconds, the rest will have no pause in between them. – niry Dec 08 '21 at 20:23
  • This **exact** solution was given here already in **2010** (By *Felix Kling*). – vsync Mar 19 '22 at 10:18
3

Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:

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)
    } 
}

For example:

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

Would be equivalent to:

//This function prints the loop number instantly
for (var i = 0; i<5; i++){
    console.log(i);
}
Dom Slee
  • 611
  • 1
  • 5
  • 10
3

To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:

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;
}

And then you call run it like so:

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

Please take some time to get a good understanding of asynchronous programming.

otboss
  • 621
  • 1
  • 7
  • 16
3

In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)

Using async/await/Promise():

alert('hi');

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

Using a generator function:

alert('hi');

let func;

(func = (function*() {
  for(let start = 1; start < 10; start++) {
    yield setTimeout(() => {
      alert('hello');
      func.next();
    }, 3000);
  }
})()).next();
niry
  • 3,238
  • 22
  • 34
2

You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.

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>
ggorlen
  • 44,755
  • 7
  • 76
  • 106
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
1

    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);

A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:

First let's define some essential variables:

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

Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:

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

Self-executing version

(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);

Functional version

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
Jasdeep Khalsa
  • 6,740
  • 8
  • 38
  • 58
1

Just try this

 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);

Result

A // after 2s
B // after 2s
C // after 2s
Shirantha Madusanka
  • 1,461
  • 1
  • 11
  • 16
  • A bit more explanation would be nice. Why should I try this relative to one of the 40 other answers here? How does it work and what merits does it have? Thanks. – ggorlen Jul 16 '22 at 21:40
0

This script works for most things

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);
}
ZomoXYZ
  • 1,763
  • 21
  • 44
0

Here is how I created an infinite loop with a delay that breaks on a certain condition:

  // 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));
    }
  }

The key here is to create a new Promise that resolves by timeout, and to await for its resolution.

Obviously you need async/await support for that. Works in Node 8.

0

for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).

        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: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.

PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..

Mohamed Abulnasr
  • 589
  • 7
  • 18
0
   let counter =1;
   for(let item in items) {
        counter++;
        setTimeout(()=>{
          //your code
        },counter*5000); //5Sec delay between each iteration
    }
Ali Azhar
  • 1,703
  • 19
  • 14
  • 1
    This disregards the ask to have a delay inside a loop. Is simply set series of events on 5 seconds interval (might as well use `setInterval`). To better understand the problem, use `alert` and wait 5 seconds before hitting OK. the next alert will show immediately, without a delay. – niry Oct 23 '20 at 06:41
0
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.
}

Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.

Oushima
  • 250
  • 3
  • 7
  • This isn't actually recursive. The call stack has to clear before the `setTimeout` callback runns. – ggorlen Jul 16 '22 at 21:33
  • From: https://techterms.com/definition/recursive_function Quote: A recursive function is a function that calls itself during its execution. The process may repeat several times, outputting the result and the end of each iteration. - This function calls itself. I don't see any reason as to why this isn't recursive. – Oushima Jul 22 '22 at 01:54
  • That's true from the code's perspective, but It's not recursive from the perspective of the application because the call stack clears completely. In most recursive functions, the call waits for the recursive child frame to return control to the parent before being able to resolve itself which doesn't happen here. It's actually a good thing because you can't blow the stack with this code versus a traditional recursive synchronous function in JS. – ggorlen Jul 22 '22 at 02:11
0

A simple, but not very elegant solution is to nest a second loop which would act as a timer (essentially, the nested loop would hold the thread). This's a dirty workaround if you're writing something temporary.

for(i=0;i<arr.length;i++){
   //you logic here

   //this will act as a timer
   for(j=0;j<25000;j++) console.log("")
}

Last week I had to download about 40 files from a website, so I wrote a short JS script to be executed through Chrome's console, and the nested loop trick did a great job in delaying the execution.

Hani
  • 753
  • 8
  • 10
-1
/* 
  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
*/
-1

Here is a function that I use for looping over an array:

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);
    }
}

You use it like this:

loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
    //Do something with item
}, function(i){
    //Do something once loop has completed
}
PJeremyMalouf
  • 613
  • 6
  • 15
-1

<!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>
  • 2
    Please always provide at least brief description to your code snippets, at least for others to be sure that you _address_ the question. – Hexfire Jan 23 '18 at 07:47
  • 2
    Code only answers arent encouraged as they dont provide much information for future readers please provide some explanation to what you have written – WhatsThePoint Jan 23 '18 at 07:58
-1

Try this...

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);
}
-1

You do it:

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>
-2

Simple implementation of showing a piece of text every two seconds as long the loop is running.

for (var i = 0; i < foo.length; i++) {
   setInterval(function(){ 
     console.log("I will appear every 2 seconds"); 
   }, 2000);
  break;
};
squeekyDave
  • 918
  • 2
  • 16
  • 35
  • This doesn't work. What's the point of a loop where you're unconditionally `break`ing on the first iteration? That can be replaced with an `if (foo.length >= 0) {...}`, but then it doesn't actually fulfill the requirements of the question. – ggorlen Jul 16 '22 at 21:35