2

My javascript skill are a bit dated, so I want to consult your for a best practice advice.

What I want to achieve:

  • Create a class that wraps around a JSON fetched from an external service
  • Create another class that acts as a factory for former class encapsulating all network stuff
  • Understand how Promises are handled in this

Here's a draft:

class DataObject {
  constructor(json) {...}
  someMethod() {...}
}

class DataObjectFactory {
  constructor() {...}
  getDataObject(id) {
     ..fetch(url)..
  }
}

myDataObjectFactory.getDataObject(123).then(...)

Now, I could force getDataObject() for act synchonously but instead i want to understand what would be the best way to make use of Promises in this case. How do I have to organize and write my stuff in order to ensure that DataObject is successfully populated with the JSON data?

Links to useful howtos also welcome.

Thank you!

Levi Haskell
  • 795
  • 8
  • 20

2 Answers2

1

There is not really much to add to what fetch and its methods already offer. But it could look like below. This snippet uses JSONPlaceholder to fetch some data:

class DataObject {
  constructor(obj) {
    Object.assign(this, obj);  
  }
  toString() {
    return `User ${this.userId} ${this.completed ? "completed" : "did not yet complete"} the task number ${this.id}, labeled '${this.title}'`;
  }
}

class DataObjectFactory {
  constructor() {
  }
  async getDataObject(id) {
     let response = await fetch("https://jsonplaceholder.typicode.com/todos/" + id);
     let obj = await response.json();
     return new DataObject(obj);
  }
}

let myDataObjectFactory = new DataObjectFactory();
myDataObjectFactory.getDataObject(123).then(myDataObject =>
    console.log(myDataObject.toString())
);

The version without async await would look like this:

class DataObject {
  constructor(obj) {
    Object.assign(this, obj);  
  }
  toString() {
    return `User ${this.userId} ${this.completed ? "completed" : "did not yet complete"} the task number ${this.id}, labeled '${this.title}'`;
  }
}

class DataObjectFactory {
  constructor() {
  }
  getDataObject(id) {
     return fetch("https://jsonplaceholder.typicode.com/todos/" + id)
          .then(response => response.json())
          .then(obj => new DataObject(obj));
  }
}

let myDataObjectFactory = new DataObjectFactory();
myDataObjectFactory.getDataObject(123).then(myDataObject =>
    console.log(myDataObject.toString())
);
trincot
  • 317,000
  • 35
  • 244
  • 286
-1

If you want to use promises directly you can make your getDataObject() to return promise that resolves after fetch is resolved. When you call getDataObject().then() give function or lambda expression what to do next.

class DataObjectFactory {
  constructor() {...}
  
  getDataObject(id) {
    let getDataObjectPromise = new Promise(function(resoveFn) {
        fetch(url).then(
            resoveFn(
                DataObject(response)
            )
        );
    });
    return getDataObjectPromise;
  }
}


function useDataObject(newDataObject){
    // there you can use DataObject
}

myDataObjectFactory.getDataObject(123).then(useDataObject); //use function or lambda expression

If you want to use natural looking syntax you can use async/await which uses promise under surface

class DataObjectFactory {
  constructor() {...}
  async getDataObject(id) {
     let json = await fetch(url);
     return DataObject(json);
  }
}

async function getAndUseDataObject(){
    var myDataObject = await myDataObjectFactory.getDataObject(123);
    // use myDataObject like in sync function
}

getAndUseDataObject(); //One can't use await outside async function
Arturs Mednis
  • 368
  • 1
  • 3
  • 12
  • (1) You are using the [promise constructor antipattern](https://stackoverflow.com/questions/23803743/what-is-the-explicit-promise-construction-antipattern-and-how-do-i-avoid-it). (2) The response object has little to do with JSON. – trincot Feb 08 '21 at 16:25