2

I would like the value of one of the properties in my class to be an asynchronous value. It almost works; when you call setTimeout, profile.songs is in fact resolved with Promise {<resolved>: Array(100)}.

Except, I want the value (in this case, Array(100)) of the Promise to be the value of property.songs.

Edit: I should add some clarity to the question. The object must be instantiated synchronously i.e. profile.toxicGarbageIsland and profile.spiceUpYourLife are mandatory, and are required for the object to instantiate. The promise value, profile.songs however, is optional, and can come after the object is instantiated.

class Gojira {
  constructor (profile) {
    Object.assign(this, profile)
  }

  static init () {
    let profile = {};

    profile.toxicGarbageIsland = true;
    profile.spiceUpYourLife = false;
    profile.songs = axios.get('https://jsonplaceholder.typicode.com/posts')
      .then(function(response){
        return response.data;   
      }).catch(function (error) {
        console.log(error)
      })

    return new Gojira(profile);
  }
}


let gojiraInstance = Gojira.init();

setTimeout(function(){
  console.log(gojiraInstance)
}, 2000)

N.B. I've removed the promise from the constructor to ensure it's only concern is with returning an object instance, which I've read is best practice.

Modermo
  • 1,852
  • 2
  • 25
  • 46
  • `static init ()` -> `static async init ()`, `profile.songs =` -> `profile.songs = await`, and `let gojiraInstance = Gojira.init()` -> `Gojira.init().then(gojiraInstance => { ... })` – Patrick Roberts May 13 '18 at 23:32
  • Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Patrick Roberts May 13 '18 at 23:32
  • @PatrickRoberts I think the reason this question is different is because it involves instantiating a new object that doesn't require async values to be immediately available. Also, that answer, whilst very good, is a general overview of promises and doesn't answer my question. – Modermo May 13 '18 at 23:45
  • What you are doing is about right. Caching the promise is the correct thing to do. Don't be talked into caching the data delivered by the promise. That's just a recipe for uncertainty. – Roamer-1888 May 14 '18 at 00:06
  • Also, don't forget to re-throw the caught error, otherwise the promise will deliver `undefined` down its success path. – Roamer-1888 May 14 '18 at 00:10

1 Answers1

1

Don't assign the promise to profile.songs - instead, simply run the promise, and on resolution, assign the result to profile.songs and call the constructor.

static init() {
  let profile = {};

  profile.toxicGarbageIsland = true;
  profile.spiceUpYourLife = false;
  return axios.get('https://jsonplaceholder.typicode.com/posts')
    .then(function(response) {
      profile.songs = response.data;
      return new Gojira(profile);
    }).catch(function(error) {
      console.log(error)
    });
}

You'll also have to consume it asynchronously:

Gojira.init()
  .then(gojiraInstance => {
    // ...
  });

I'm assuming the constructor requires the profile to be fully set before running. If not, then allow the constructor to accept a Promise as a parameter as well, which then assigns the songs property on resolution, like this:

class Gojira {
  constructor (profile, songsPromise) {
    Object.assign(this, profile);
    if (songsPromise) songsPromise.then(({ data }) => this.songs = data);
  }
  static init () {
    const profile = {};
    profile.toxicGarbageIsland = true;
    profile.spiceUpYourLife = false;
    const songsPromise = axios.get('https://jsonplaceholder.typicode.com/posts')
      .catch(error => console.log(error));
    return new Gojira(profile, songsPromise);
  }
}
const gojiraInstance = Gojira.init();
setTimeout(function(){
  console.log(gojiraInstance)
}, 2000)
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • To that end, you could even initialize `profile` inside the `promise.then()` if you decide to do this – Patrick Roberts May 13 '18 at 23:34
  • Actually, the constructor doesn't require `profile` to be fully set before running, only `profile.toxicGarbageIsland` and `profile.spiceUpYourLife` are mandatory. `profile.songs` is optional at the point of instantiation, because I don't want its absence to block the object from being created (and rendered on screen). I'm happy for `profile.songs` to come later. – Modermo May 13 '18 at 23:42
  • @CertainPerformance could you clarify what you mean by " allow the constructor to accept a Promise as a parameter as well"? – Modermo May 14 '18 at 00:09