0

I have an object with an array property which I want to fill with the responses received from an asynchronous call. This call is being made by a method from a 3rd party library that receives a call back function, so I tried something like this:

class Foo {
    constructor() {
        this.myArray = [];

        thirdPartyObject.thirdPartyMethod(
            param1,
            param2,
            function(data){ //success callback
                data.forEach(item => {
                    var myArrayEle = {
                        propertyA : item.propertyA,
                        propertyB : item.propertyB
                    };
                    this.myArray.push(myArrayEle);
                })
            },
            function(data){ //error callback
                throw "Error"
            }
        )
    }
}

I get an error on this.SomeArray.push(myArrayEle); saying "cannot read property myArray of undefined" though.

I don't want to simply do stuff with the response then let it go as I want to keep this array stored so I can do stuff with it depending on what the user does instead of having to make another call later on to get the same data.

Is there a way to do this inside the constructor or do I have to use promises somewhere else to do this?

Janilson
  • 1,042
  • 1
  • 9
  • 23
  • Constructors should return concrete Objects. A better way to do this is to create a factory function that creates `Foo`s but returns a `Promise` that will resolve after the `Foo` is fully populated. – zero298 Jul 17 '18 at 18:51
  • 4
    However, **your** issue is that `this` is the wrong `this`. Use an arrow function for your callback in `thirdPartyMethod` instead. – zero298 Jul 17 '18 at 18:52
  • As mentioned above it is kind of a bad practice. You can read more here: https://stackoverflow.com/questions/24398699/is-it-bad-practice-to-have-a-constructor-function-return-a-promise – Laurynas Jul 17 '18 at 18:54
  • 1
    The basic idea is broken. You won't be able to access `.myArray` until after the async operation is finished. But with the current setup (even after you fix the other problems) there's no way to know when that will be. – Mark Jul 17 '18 at 18:54

1 Answers1

2

One way to handle this is to have a constructor on your class that just sets things up and a different init function that can handle getting data asynchronously and let you know when the object is properly initialized. Here's an example where we mock your thirdPartyMethod. When you call the constructor, the array will be undefined, but you can call init().then() to know when it's ready:

const thirdPartyObject = {
  thirdPartyMethod(param1, param2, success, error) {
    setTimeout(() => success([{propertyA: "propA0",propertyB: "propB0"}, {propertyA: "propA1",propertyB: "propB1"
    }]), 1000)
  }
}

class Foo {
  constructor(data) {
    console.log("making instance")
    this.myArray = data;
  }
  init() {
    console.log("starting init")
    return new Promise((resolve, reject) => {
      thirdPartyObject.thirdPartyMethod(
        'param1','param2',
        (data) => { //success callback    // use arrow function so 'this' works 
          this.myArray = data.map(item => {
            return {
              propertyA: item.propertyA,
              propertyB: item.propertyB
            };
          })
          console.log("finished init, data ready")
          resolve(true)
        },
        function(data) { //error callback
          reject("Error")
        }
      )
    })
  }
}

let f = new Foo()
f.init()
  .then(() => console.log(f.myArray))
  .catch(err => console.log(err))
Mark
  • 90,562
  • 7
  • 108
  • 148