1

With JavaScript, when creating a class, while instantiating that class, I want to populate a public property. I can do this using a setter, however, the value I want comes from an external website which I retrieve via an ajax get call. The issue becomes that the new class object does not have the appropriate property value when I create it. Here's some sample code:

class MyTestClass {
  constructor() {
    this._ipaddress = "";
  }
  get ip() {
    return this._ipaddress;
  }
  set ip(value) {
    createIpAddr();
  }
  createIpAddr() {
    var myIpAddr = "";
    var strUrl = "https://api.ipify.org/?format=json";
    $.ajax({
      url: strUrl,
      success: function(data) {
        this._ip = data.ip;
      },
      //async:false //Performing this in sync mode to make sure I get an IP before the rest of the page loads.
    });
    return myIpAddr;
  }
}

var testobj = new MyTestClass();
console.log(testobj.ip);

The problem here is that I can't be sure the IP will be populated in time to use after creating the new instance of the class. I've tried promises and deffered, but they have the same problem, I can't populate the variable before I need it. I'm trying to adjust the way I am looking at this and adding callbacks, but the issue is that I need the correct value in the class before I can use the class for the next call, where I am passing this object to it.

Is there a simple solution I am over looking? I have been through a million of these threads about async: false, and I don't want to start a new one, but what is a better choice in this case?

I want to set a class property from an ajax response when instantiating the class object.

Tyler Roper
  • 21,445
  • 6
  • 33
  • 56
MrTaz
  • 11
  • 1
  • `_ip` vs `_ipaddress`? – Bergi Feb 20 '19 at 20:56
  • Have a look at [this question](https://stackoverflow.com/q/24398699/1048572). Don't make the ajax request within the class, make it *before* instantiating the class. And yes, that means that you will need to change the code that creates the class, but you need to do that anyway - you cannot do this synchronously. – Bergi Feb 20 '19 at 20:58
  • Here's one way: https://jsfiddle.net/khrismuc/6ogLaujp/ –  Feb 20 '19 at 21:07
  • [this](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback) and [this](https://stackoverflow.com/questions/23667086/why-is-my-variable-undefined-after-i-modify-it-inside-of-a-function-asynchron) are both relevant. – Barmar Feb 20 '19 at 21:30

1 Answers1

1

You could have your constructor return an async IIFE, allowing you to then await the creation of a new class instance.

That would look something like this:

class MyTestClassAsync {
  constructor() {
    return (async() => {
      this._ip = (await this.createIpAddrAsync()).ip;
      return this;
    })();
  }
  get ip() {
    return this._ip;
  }
  set ip(value) {
    this._ip = value;
  }
  createIpAddrAsync = () => $.get("https://api.ipify.org/?format=json");
}

async function Main() {
  var testobj = await new MyTestClassAsync();
  console.log(testobj.ip);
}

Main();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

I've made the personal decision to append "Async" to the method and class names, just so that it's clear they need to be awaited.

Tyler Roper
  • 21,445
  • 6
  • 33
  • 56