1

I have a class with this structure:

class MyClass{
    json;

    constructor(){

    }
}

I would like to assign my "json" property form an API request in the constructor as an Array.

I've tried all sorts of methods and even copied code snippets directly from other forums. In the debugger and from console.log(), I've confirmed that I'm actually getting a response and in the Promise.prototype.then() I was able to use the result. But I just can't get it assigned to a class property.

These examples don't work:

class MyClass{
    json;

    constructor(){
        fetch(url)
            .then(response => response.json())
                .then(json =>  {
                this.json = json;   // I've tried using "self" when not using the "=>" operator
            });
    }
}
class MyClass{
    json;

    constructor(){
        fetch(url)
            .then(response => response.json())
                .then(json =>  {
                this._setJson(json);
            });
    }

    _setJson(json){
        this.json = json;
    }
}

I've also tried initializing json as an Array and using this.json.push(json) or returning json as an Object. All the times this.json never gets assigned and I get ReferenceError: json is not defined↵ at eval (...

I would like it to be assigned but clearly, it isn't. Also, I'm using Chrome 75 if that makes any difference. - Thanks

yak27
  • 94
  • 8
  • How and when are you accessing/using that `json`? Take into account that the property will only have a value once the request is finished. – mgarcia Jun 28 '19 at 19:22
  • It's data critical to that class. I would like to use it later in the constructor as well as in other methods. I've tried throwing `async` and `await` keywords around, but I'll be the first to admit that I don't fully understand their implications. – yak27 Jun 28 '19 at 19:51
  • I recommend you not to perform asynchronous tasks in class constructors. If the data you are fetching is vital for the class, I would fetch it outside of the class and, once the data is available, create an instance of the class passing it the data. `asnyc` and `await` make your code look like it is synchronous and its easier to read but the code continues to be asynchronous so you would face the same problem. – mgarcia Jun 28 '19 at 21:33
  • Thanks, I'll see how that turns out. – yak27 Jun 28 '19 at 22:51
  • Yes, I'm pretty sure it's a sync issue. When I access a method from that object from the console - after everything has been run - I get `json` populated. – yak27 Jun 28 '19 at 23:01
  • Yes, it is a timing issue. See the answer in the question yours has been marked a duplicate of as it outlines the various choices you have. Because your operation is asynchronous, it won't be done when the constructor returns and because you can't return a promise from the constructor (it has to return the object), you have to develop other ways of running the async operation or knowing when it's done. That other answer outlines your choices. – jfriend00 Jun 29 '19 at 23:43

2 Answers2

0

Your structure looks to be slightly off:

class MyClass{
    constructor() {
        this.json = null;

        //fetch logic here
    }

    _setJson(json){
        this.json = json
    }
}
Pytth
  • 4,008
  • 24
  • 29
  • I'm using Field Declarations, Chrome 75 supports it. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes#Field_declarations – yak27 Jun 28 '19 at 19:02
0

When callback is executed this points to global scope instead of MyClass scope. To fix this issue, save object's context into some variable like self:

class MyClass {
    json;

    getJson() {
        return this.json;
    }

    constructor(myCallback) {
        var self = this;
        fetch("https://www.mocky.io/v2/5185415ba171ea3a00704eed").then(function(response) {
            return response.json().then(function(json) {
                alert(this == myClassInstance); // false
                alert(self == myClassInstance); // true
                self.json = JSON.stringify(json);
                myCallback();
            });
        });
    }
}

var myCallBackImpl = () => { document.getElementById("response").innerHTML = myClassInstance.getJson(); }
var myClassInstance = new MyClass(myCallBackImpl);
Marcelo Vismari
  • 1,147
  • 7
  • 15
  • As I've said "I've tried using "self" when not using the "=>" operator". – yak27 Jun 28 '19 at 19:00
  • Even using arrow function it works properly. I have tested this snippet on Chrome 75.0.3770. – Marcelo Vismari Jun 28 '19 at 19:05
  • What does `myCallback` do? Because if I strip it out it behaves like my code, and my code doesn't have these callback stuff. Thanks – yak27 Jun 28 '19 at 19:45
  • Callback is just an example, you can take it out of the code, but keep in mind that `json` will get a value when` fetch` finishes running – Marcelo Vismari Jun 28 '19 at 21:53
  • Yes, I'm pretty sure it's a sync issue. When I access a method from that object from the console - after everything has been run - I get `json` populated. Thank you for all your help. – yak27 Jun 28 '19 at 23:02