-1

In this class for an angular component I'm having a scope issue that I don't understand.

class ConnectionsComponent {
  constructor($http, $q) {
    this.$http = $http;
    this.$q = $q;
    this.listGotten = [];
    this.arrayOfGets = ['id1', 'id2', 'id3'];
  }

  $onInit() {
    var promises = [];

    angular.forEach(this.arrayOfGets, getThis => {
      var promise = this.$http.get('/api/endpoint/' + getThis)
      promises.push(promise)
    }) // end angular.forEach

    this.$q.all(promises).then((data) => {
      this.listGotten = data;
      console.log(this.listGotten) // <-- prints array of objects
    })

      console.log(this.listGotten) // <-- empty array (PROBLEM!)    

  } // end $oninit

} // end class

According to this post, it shouldn't be an issue because I'm using the arrow function which passes the scope into the then(). It's NOT undefined, it's just an empty array, as if this.listGotten never had data assigned to it.

Community
  • 1
  • 1
Gabriel Kunkel
  • 2,643
  • 5
  • 25
  • 47

2 Answers2

2

This happens because this.$q.all itself it's a promise (asynchronous) that will be resolved when all of the input promises are resolved. So, when you write this:

this.$q.all(promises).then((data) => {
  this.listGotten = data;
  console.log(this.listGotten) // <-- prints array of objects
})

//This will be executed before the $q.all is resolved or rejected !
//or at least will not wait for $q.all to finish
console.log(this.listGotten) // <-- empty array (PROBLEM!)  
Ismail RBOUH
  • 10,292
  • 2
  • 24
  • 36
1

It's a matter of timing. Your .then() handler is called LATER because it represent the completion of all your asynchronous operations. Whereas, your:

console.log(this.listGotten) // <-- empty array (PROBLEM!)  

is executed before your .then() handler has even run so this.listGotten has not yet been set. Your arrow functions are working as expected - this is not an issue related to that.

If you put some console.log() statements into your code to see the actual timing of things like this:

  $onInit() {
    var promises = [];
    console.log("1 - start");   
    angular.forEach(this.arrayOfGets, getThis => {
      var promise = this.$http.get('/api/endpoint/' + getThis)
      promises.push(promise)
    }) // end angular.forEach

    console.log("2 - about to do .all()");
    this.$q.all(promises).then((data) => {
      console.log("3 - inside .then() handler");
      this.listGotten = data;
      console.log(this.listGotten) // <-- prints array of objects
    })

      console.log("4 - after .then() handler");
      console.log(this.listGotten) // <-- empty array (PROBLEM!)    

  } // end $oninit

Then, you will see this output:

1 - start
2 - about to do .all()
4 - after .then() handler
3 - inside .then() handler

So, you can see that your .then() handler is called after your other console.log() executes.

The solution for asynchronous results is to "use them in the callback where they are presented" because that's the ONLY place you know the timing for when they exist.

Remember in asynchronous code, all your $http.get() operations just initiate the http requests and then they run in the background. The rest of your Javascript continues to run and some time LATER your asynchronous requests will finish and call their callbacks, long after the rest of your code has finished executing.

jfriend00
  • 683,504
  • 96
  • 985
  • 979