Search code examples
typescriptservicepropertiestry-catch

How to set multiple properties without multiple try catch blocks and calls multiple services - Typescript?


I am gathering data to set for a list of properties, however it calls many different services to help obtain this data. If there is a problem with any of the services it would throw an error. If an error is thrown I want the data to just return null for that specific property, and continue getting the rest of the data.

How do I do this without wrapping each service call in a try catch block?

I.E) this get all the properties, but if anything fails, the properties afterwards will not be set.

public async getAllProperties(){
try {
 const [studentYear, numOfCourses, numOfSemesters, startDate, gradDate] = 
await Promise.all([
   studentService.getStudentYear(), 
   courseService.getNumOfCourses(),
   semesterService.getNumOfSemesters(), 
   .... (etc)
])} catch (e){
   console.log("error with getting Properties");
 }
}

For example if I call getAllProperties() and the courseService throws an error, I want numOfCourses to be null, and for it to continue to check semesterService.getNumofCourse and continue setting the rest of the properties.

How can I do this without try catch for each service? I.E I don't want to write this for each property

try {
studentService.getStudentYear
} catch (e){
console.log("error in student Year");
studentYear = null;
}

try {
courseService.getNumOfCourses()
} catch (e){
console.log("error in get Num Of courses");
numOfCourses = null;
}

Thanks for any assistance or ideas!


Solution

  • I like Alex's solution. But incase the resulting {status: "fullfilled", value: 2018} is a problem, here's my proposed solution:

    var obj = {
      getStudentYear: () => new Promise(res => res(2018)),
      getNumOfCourses: () => new Promise(res => res(8)),
      getFailed: () => new Promise( (res, rej) => rej('failed'))
    }
    
    async function resolveOrNull(func){
      try{
        let x = await func()
        return x;
      } catch(err) {
        console.log(err);
        return null;
      }
    }
    
    async function getAllProperties() {
      return {
        studentYear: await resolveOrNull(obj.getStudentYear),
        numOfCourses: await resolveOrNull(obj.getNumOfCourses),
        getFailed: await resolveOrNull(obj.getFailed)
      }
    }
    
    getAllProperties()
      .then(res => console.log(res))
      .catch(err => console.log(err))
    

    Edit: So let's say you wanted to make a test to your code. You mock the functionality and now you want to make sure the resulting studentYear was equal to an expected value of 2018:

    If you're using a primitive value you can just do this:

     let studentYear = test.getStudentYear() // returns 2018
     studentYear === 2018 // this is TRUE
    

    If you're using an object:

    let studentYear = test.getStudentYear() // returns {status: "fullfilled", value: 2018}
    studentYear === {status: "fullfilled", year: 2018} // this is FALSE
    

    When Javascript compares one object to another object, it's compared by it's reference, not by it's values. I've run into plenty of other headache's when using objest/arrays and try my best to avoid it when possible. Which it often isn't, and honestly it's usually fine, but they do tend to over complicate things that could otherwise be very simple.

    Edit2: (This is starting to get a bit "sloppy")

    //... previous code
    let globalVariable;
    
    getAllProperties()
      .then((res) => {
        globalVariable = res;
        useGlobal();
      })
      .catch((err) => console.log(err));
    
    function useGlobal() {
      console.log(globalVariable); // This contains the correct obj
      //  anything else that uses the global variable 
      //    should be called inside this function
    }
    
    console.log(globalVariable); // This is undefined
    

    Edit3: (This is more along the lines of what you should be doing, but there's quite a few different ways to handle the problems that come with async code)

    getAllProperties()
      .then(useAllProperties)
      .catch((err) => console.log(err));
    
    function useAllProperties = (res) => {
      console.log(res);
      // run code that needs the data
    }