1

Newbie alert!

I'm trying to understand the variable scope for a promise inside a for loop.

Consider the following code:

function sleep(ms) {
  return new Promise(resolve =>
    setTimeout(resolve, ms)
  );
}

for (var i =0; i<4; i++){
  sleep(1000).then(() => console.log(i));
}

The promise is called 4 times at once and then responds with:

4
4
4
4

How can I keep reference to the i variable? In order to get

0
1
2
3

Or whatever execution order actually took place.

Ben Quan
  • 773
  • 11
  • 25
  • 4
    Possible duplicate of [JavaScript closure inside loops – simple practical example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – ASDFGerte Mar 14 '18 at 17:04
  • 2
    The easiest solution is to replace `var` with `let`, but you should read up on why. – ASDFGerte Mar 14 '18 at 17:05
  • wow... Thanks... that did the trick. On my way to understand the differences. – Ben Quan Mar 14 '18 at 17:11
  • Using let does cause the console to print the correct value but it does not give, what I expect to be, the intended performance: printing the value 1 second apart in incrementing values. – Thatalent Mar 14 '18 at 17:29
  • Simultaneous execution is fine... All I needed is the reference on what just got processed. – Ben Quan Mar 14 '18 at 18:54

3 Answers3

1

You can pass the i into the sleep function and resolve it.

 function sleep(i, ms) {
   return new Promise(resolve =>
     setTimeout(() => {
       return resolve(i);
     }, ms);
   );
 }

 for (var i = 0; i < 4; i++){
   sleep(i, 1000).then((data) => console.log(data));
 }
XPX-Gloom
  • 601
  • 5
  • 11
1

Your problem is just how you're calling your sleep function. You must use the await keyword inside of an async labeled function.

This should be how your code looks:

function sleep(ms) {
  return new Promise(resolve =>
    setTimeout(resolve, ms)
  );
}

async function printSleep(){
for (var i =0; i<4; i++){
  await sleep(1000).then(() => console.log(i));
}
}

printSleep();

The key part you're missing is that you must use await to actually have your code wait for the sleep function to finish. If you don't it will queue the function and continuing executing the rest of the function in a synchronous fashion, causing your then statement to execute after the for loop has finished.

Thatalent
  • 404
  • 2
  • 8
0

As pointed out by ASDFGerte, by changing var to let, the i variable is kept within the block and outputs the desired reference.

function sleep(ms) {
  return new Promise(resolve =>
    setTimeout(resolve, ms)
  );
}

for (let i=0; i<4; i++){
  sleep(1000).then(() => console.log(i));
}
Ben Quan
  • 773
  • 11
  • 25