Understanding while-loops in Vue with setTimeout

Viewed 9774

I'm quite new with Vue. This is confusing the hell out of me.

If I have this while-loop inside of a method.

methods: {
  test: function() {
    counter = 0;
    while( counter < 10 ){
      console.log( counter );
      counter++;
      window.setTimeout( function() {
        console.log( 'Test' );
      }, 1000)
    }
  }
},
mounted() {
  this.test();
}

Then in my console, it will print this:

0
1
2
3
4
5
6
7
8
9

(10) Test

Correct me if I'm wrong, but shouldn't it write this:

0
Test
1
Test
2
Test
3
Test
4
Test
5
Test
6
Test
7
Test
8
Test
9
Test

... And do it with a seconds delay in-between?

The reason why I'm asking is that I'm pulling data from an API, - and only want to initialize a function, when the data has been populated.

This post, suggest using arrow-functions for the setTimeout, but I'm not seeing any difference when doing it.

And I've looked a lot at Vue's lifecycle, but that didn't show me the answer either.

2 Answers

Counting in the while loop happens really fast (< 1 second), so by the time the timeout executes the rest of your while loop has already executed (printing 0 to 9), after that your timeouts reach their countdown and also print 'Test' consecutively. This causes it to be printed 10 times after each other which in the console is abbreviated with the prefix (10) instead of printing test literally 10 times.

This happens because when you call window.setTimeout the code called here will be executed after x miliseconds in parallel, thus the rest of your code continues to process while the timeout is counting down.

If you want the result you expected you should execute it directly and not use a timeout:

methods: {
  test: function() {
    counter = 0;
    while( counter < 10 ){
      console.log( counter );
      counter++;
      console.log( 'Test' );
    }
  }
},
mounted() {
  this.test();
}

If you want to wait 1 second between every number you should use a recursive function, something like this:

test(0);

function test (counter) {
    if (counter < 10) {
       console.log( counter );
       counter++;
       console.log( 'Test' );
       window.setTimeout( function() {
          test(counter);
       }, 1000)
    }
}

Make sure to use an initial or default value of 0 for this

The best you can use is a Promise. The getApi function should be modified and read the API and using a promise to execute it in parallel, so I would call the then once the load is finished...

methods: {
  test: function() {
    this.getAPI().then((data) => {
       // here is the API load finished | data === 'example data' 
    });
  },
  getAPI: function(){
    // change this
    return new Promise((resolve) => {
      resolve('example data');
    });
  }

},
mounted() {
  this.test();
}
Related