1

I want to hold nodejs execution in setTimeout inside while loop. I have used async- waterfall function but it didn't work inside while loop. so I have used below code :-

    var i = 3;

    while(i> 0)
    {
        setTimeout(function(){
            console.log('start', i);

            setTimeout(function(){ console.log('timeout');}, 1000);

            console.log('end', i);

            i--;
        }, 1000);

    }
console.log("execution ends");

But I didn't get the expected output. My Expected output will be like :-

    start3
    timeout
    end3
    start2
    timeout
    end2
    start1
    timeout
    end1
    execution ends
2
  • Do you realize that setTimeout() is NOT blocking. It just schedules something to run in the future and then the rest of your code continues to run. That means your while loop keeps running forever setting timers and never giving them a chance to run. Commented Apr 12, 2018 at 7:13
  • don't run the while loop!! Commented Apr 12, 2018 at 7:14

6 Answers 6

2

way 1:use while loop

var i = 3
var p = Promise.resolve(i)
while (i > 0) {
  (i => {
    p = p.then(() => {
      return new Promise(function (resolve, reject) {
        console.log('start', i)
        setTimeout(function () {
          console.log('timeout')
          console.log('end', i)
          resolve()
        }, 1000)
      })
    })
  })(i)
  i--
}
p = p.then(data => console.log('execution ends'))

way2:

function test(i) {
  console.log('start', i)
  setTimeout(() => {
    console.log('timeout')
    console.log('end', i)
    i--
    if (i < 0) {
      return
    }
    test(i)
  }, 1000)
}
test(3);

way3:use async

async function test (i) {
  console.log('start', i)
  await new Promise(function (resolve, reject) {
    setTimeout(() => {
      console.log('timeout')
      console.log('end', i)
      i--
      if (i < 0) {
        reject(i)
      }
      resolve(i)
    }, 1000)
  })
    .then(i => test(i))
    .catch(i => console.log('done', i))
}
test(3)

Sign up to request clarification or add additional context in comments.

17 Comments

What do you think await setTimeout() does? Hint, await only actually waits if you give it a promise, not a timerId.
This really isn't the right way to program. The async and the await are not doing anything useful here at all. In fact, you can remove both of them. The recursive solution is one approach to this problem, but teaching this use of async and await is just wrong.
Can get the same output using while loop only?
@jfriend00,sorry about making a mistake.Updated now,what about the second one?
@KetanBodarya,the first one is your choice,don't you think so?
|
0

nodejs is async by nature and setTimeout is more or less like executing something on a new thread (more or less because JS is single threaded and uses event loop).

Check out this npm package : https://www.npmjs.com/package/sleep

That should do the trick...
But obviously you should use sleep only for debugging purposes. In production code you'd better embrase the async nature of NodeJS and use Promises

Check out this for more details: event loop : https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

setTimeout : https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout

Promise : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

5 Comments

setTimeout() is NOT like executing something on a new thread at all. It's non-blocking. It's just scheduling something to run sometime in the future. It's like putting a reminder in your calendar. The rest of your life keeps going and then some time in the future, the reminder fires. And teaching a node.js newbie to solve a problem with sleep() is a really, really bad idea. One HAS to learn proper async development to write successful code in node.js.
I over simplified it... you saw the "more or less" ? And OP asked for a sleep... if you use that for debugging purposes there's nothing wrong with it...
Well, it's not "more or less" like it at all. And your recommendation to use sleep() is just the wrong way to solve any async problem in node.js except for a rare debugging issue. Even the sleep NPM page says it's mainly for debugging because it blocks the event loop. Any code that blocks the event loop in node.js is really bad for any type of server development as it ruins all scalability of the server. It is not what you should teach a newbie as it should never be used in a server design.
The OP is asking how to write proper code in node.js that generates a particular result. They aren't asking how to debug something. sleep() is the wrong recommendation.
first sentence is "I want to hold nodejs execution"... But anyway I updated the answer to warn about that and added a note about Promise
0

There are a few issues with your program w.r.t to your expected output. The first i-- will work only after 1000ms. By that time, too many while loops would have run. Also setTimeOut is non-blocking ,hence ,the execution ends will be consoled even before the first start is consoled . To reach your expected outcome, you can make a few changes to the code :

var i = 3;var j =3;

    while(j> 0)
    {
        setTimeout(function(){
            console.log('start', i);
            console.log('timeout');
            console.log('end', i);

            i--;
        }, 1000);
        j--;
    }
    if(j == 0){
        setTimeout(function(){
            console.log('execution ends');
        }, 1000);


    }

Comments

0

Please try below snippet. You can introduce API calls OR async calls in place of setTimeout in timer method.

const timer = () => {
    return new Promise(res => {
        setTimeout(() => {
            console.log('timeout');
            res();
        }, 1000);
    });
}

let i = 3;
let temp;

while (i > 0) {
    if (temp !== i) {
        temp = i;
        console.log('start', temp);
        timer().then(() => {
            console.log('end', temp);
            i -= 1;
        });
    }
}

Comments

0

First off, you have to understand that setTimeout() in Javascript is non-blocking. That means that all it does is schedule something to run later and then the rest of your code immediately keeps on running.

In your particular code example, you will have an infinite loop because the while loop keeps going forever, continuing to schedule more and more timers, but until the while loop stops, none of those timers can run. And, until one of those timers can run, your loop variable i never gets changed so the while loop never stops.

To understand why it works this way you really have to understand the event-driven design of Javascript and node.js. When you call setTimeout(), it schedules an internal timer inside of the JS engine. When that timer fires, it inserts an event in the Javascript event queue. The next time the JS interpreter is done with what it was doing, it will check the event queue and pull the next event out of the queue and run it. But, your while loop never stops going so it can't ever get to any new events, thus it can never run any of your timer events.

I will show you three different ways to generate your desired output and they are all in runnable code snippets so you can run them right in the answer to see their results.

The first technique is accomplished in plain Javascript just by setting timers at different times from the future such that the various desired outputs trigger in the right sequence:

Varying Timers

let cntr = 3;
for (let i = 1; i <= 3; i++) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
}

Or, if you really want it to be a while loop:

let cntr = 3, i = 1;
while (i <= 3) {
    // schedule timers at different increasing delays
    setTimeout(function() {
        console.log('start', cntr);
        setTimeout(function() {
            console.log('timeout');
            console.log('end', cntr);
            --cntr;
            if (cntr === 0) {
                console.log("execution ends");
            }
        }, 1000);
    }, i * 2000);
    i++;
}

Using Promises to Sequence Operations

function delay(t, v) {
    return new Promise(resolve => {
        setTimeout(resolve.bind(null, v), t);
    });
}

function run(i) {
    return delay(1000).then(() => {
        console.log("start", i);
        return delay(1000).then(() => {
            console.log("timeout");
            console.log("end", i);
            return i - 1;
        });
    });
}

run(3).then(run).then(run).then(() => {
    console.log("execution ends");
});

This could also be put into a loop if you wanted:

function delay(t, v) {
   return new Promise(resolve => {
       setTimeout(resolve.bind(null, v), t);
   });
}

function run(i) {
    return delay(1000).then(() => {
      console.log("start", i);
      return delay(1000).then(() => {
          console.log("timeout");
          console.log("end", i);
          return i - 1;
      });
    });
}

[3, 2, 1].reduce((p, v) => {
     return p.then(() => {
         return run(v);
     });
}, Promise.resolve()).then(() => {
    console.log("execution ends");
});

Promises Plus ES7 Async/Await

This method uses the await feature of ES7 to allow you to write sequential-like code using promises and await which can make these kinds of loops a lot simpler. await blocks the internal execution of a function while it waits for a promise to resolve. It does not block the external return of the function. the function still returns immediately. It returns a promise that resolves when all the blocked pieces of the internal function are done. That's why we use .then() on the result of runSequence() to know when it's all done.

function delay(t, v) {
   return new Promise(resolve => {
       setTimeout(resolve.bind(null, v), t);
   });
}

async function runSequence(num) {
    for (let i = num; i > 0; i--) {
        await delay(1000);
        console.log("start", i);
        await delay(1000);
        console.log("timeout");
        console.log("end", i);
    }
}

runSequence(3).then(() => {
    console.log("execution ends");
});

This await example illustrates how promises and await can simplify the sequencing of operations in ES7. But, they still require you to understand how asynchronous operations work in Javascript, so please don't attempt to skip that level of understanding first.

Comments

-1

you didn't got the expected output because there is closure in your code,improve your code like this:

var i = 3;
            while(i> 0)
            {
                setTimeout((
                    function(i){
                        return function(){
                            console.log('start', i);
                            setTimeout(function(){ console.log('timeout');}, 1000);
                            console.log('end', i);
                            i--;
                        }
                    }
                )(i), 1000);
            }

6 Comments

Did you try this yourself? How many times does it go through the while loop?
you want to output the value of i?
In this code debugger stucked between while loop in first setTimeout.
The OP shows the expected output they wanted. Did you actually run your code. Does your code generate that expected output? Hint, this is an infinite while loop and no timer ever gets a chance to fire. This is just wrong. You should try to run it yourself (I did).
@zero I want exact the same output as I mentioned in question using the while loop.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.