0

I am trying to figure out what exactly I am missing with these asynch calls. I have a Page class which instantiates a weather object which makes an API call but the program continues to run before the object loads.

When I create a new page instance, Promise {<pending>} prints to the console then Uncaught TypeError: Cannot read property 'hourly' of undefined then the fetched weather data prints to the console. It is failing when I try to make the new Rain object because the Weather object instantiation hasn't resolved. Here is the code that is being run:

Page class:

class Page{
    constructor(){
        this.time = new Time();
        this.weather = new Weather('/weather');
        console.log(this.weather.data)
        this.rain = new Rain(this.weather.data.end.hourly.data);
    }

Weather class:

class Weather{
    constructor(route){
        this.route = route;
        this.data = this.get();
        // this.data = (async () => await this.get());
    }
    async get(){
        const response = await fetch(this.route);
        const data = await response.json();
        console.log(data);
        return data
    }

I also tried setting this.data in Weather to this.data = (async () => await this.get());. That returns the same error but this time async () => await this.get() prints to the console instead of Promise {<pending>}

I feel like I am missing the point where I need to use await.

I have also tried making the constructor call in Page an async init function where this.weather = await new Weather('/weather') but it still returns Uncaught (in promise) TypeError: Cannot read property 'hourly' of undefined

class Page{
    constructor(){
        this.init();
    }

    async init(){
        this.time = new Time();
        this.weather = await new Weather('/weather');
        console.log(this.weather.data)
        this.rain = new Rain(this.weather.data.end.hourly.data);
    }

Where I am going wrong? In an earlier version of the code, where I did everything from the weather class, it actually worked fine.

class Weather{
    constructor(route){
        this.route = route;
        this.data = null;
        this.hour_forecast = {};
        this.rain = {
            total_prob: null,
            max_prob: null,
            max_prob_time: null,
            max_intensity: null,
            max_intensity_time: null
        };
    }

    async init(){
        await this.get();
        this.predictRain();
        this.render();
    }

    async get(){
        const response = await fetch(this.route);
        this.data = await response.json();
        this.hourly_forecast = this.data.end.hourly.data.slice(0, 11); //gets next 12 hours
        console.log(this.data);
    }

I was calling the init() after making the weather instance and the get function was setting rather than returning the value but I have been unable to use that information to figure out what I could do differently in the refactored code.

nonethewiser
  • 550
  • 1
  • 6
  • 18
  • `console.log(await this.weather.data)` – Lennholm Apr 12 '20 at 00:31
  • I'm not sure why, but that results in "Uncaught SyntaxError: missing ) after argument list". Maybe I missed your point, but the problem is not that the console.log prints late, it's that this.weather.data.end.hourly.data is undefined when it's referenced. The console.log returning after the error just proves that this.weather.data.end.hourly.data is a valid reference – nonethewiser Apr 12 '20 at 00:39
  • You need to make the function where you `await` an `async` function, of course. Like you did with your `init()` function.`this.data` in the `Weather` instance is a promise. This is the thing you need to `await`. I put the `await` in the `console.log()` just to show this, but what you really need to do is this: `this.rain = new Rain((await this.weather.data).end.hourly.data)` – Lennholm Apr 12 '20 at 00:48
  • The short answer, is you don't want to put an asynchronous operation in a constructor. The constructor needs to return the object so you can't return a promise to know when it's done with the asynchronous part. Therefore, you'd have to hack in some other means of knowing (like a promise in the instance data). If you have an object that needs some asynchronous operation before the object is usable and initialized, then don't expose the constructor. Instead, expose a factory function that returns a promise that resolve to your finished object. – jfriend00 Apr 12 '20 at 02:07
  • See [Asynchronous operations in constructor](https://stackoverflow.com/questions/49905178/asynchronous-operations-in-constructor/49906064#49906064) which is one of the questions yours is marked a duplicate of for various options, including the factory function. – jfriend00 Apr 12 '20 at 02:08
  • Thank you jfriend00 and @Lennholm for the informative responses. – nonethewiser Apr 12 '20 at 02:29

0 Answers0