Hur man ställer in flera egenskaper utan flera try-catch-block och anropar flera tjänster - Typescript?


Jag samlar in data för att ställa in för en lista med egenskaper, men det anropar många olika tjänster för att hjälpa till att hämta denna data. Om det uppstår ett problem med någon av tjänsterna skulle det kasta ett fel. Om ett fel kastas vill jag att datan bara ska returnera null för den specifika egenskapen, och fortsätta hämta resten av datan.

Hur gör jag detta utan att lägga varje tjänsteanrop i en try catch-block?

D.v.s.) detta hämtar alla egenskaper, men om något misslyckas kommer egenskaperna därefter inte att sättas.

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");
 }
}

Till exempel, om jag anropar getAllProperties() och courseService kastar ett fel, vill jag att numOfCourses ska vara null, och att det fortsätter att kontrollera semesterService.getNumofCourse och fortsätter att sätta resterande egenskaper.

Hur kan jag göra detta utan try catch för varje tjänst? D.v.s. jag vill inte skriva detta för varje egenskap

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;
}

Tack för all hjälp eller idéer!

Svar #1

Jag skulle föreslå Promise.allSettled. Denna metod returnerar ett löfte som löses när alla givna löften antingen har uppfyllts eller avvisats, med en array av objekt som var och en beskriver resultatet av varje löfte. Källa

Varje promise måste använda try catch för att upptäcka ett fel och returnera null om det finns ett fel. Du kan skapa en funktion som kommer att vänta på ett Promise och antingen returnera resultatet eller null om det finns ett fel. På detta sätt behöver du bara använda try catch en gång.

Här är ett kodexempel på dess användning. https://stackblitz.com/edit/typescript-1nkhf4

function getAllProperties() {
  Promise.allSettled([getName(), getAge()]).then(data => {
    console.log(data)
  })
}

function getName() {
  return returnNullIfFail(() => new Promise((res, rej) => res("some name")))
}
function getAge() {
  return returnNullIfFail(() => new Promise((res, rej) => rej("some error")))
}

async function returnNullIfFail<T>(somePromise: () => Promise<T>) : Promise<T | null> {
  try {
    return await somePromise()
  } catch (err) {
    return null
  }
}

getAllProperties()

Svar #2

Jag gillar Alex lösning. Men ifall det resulterande {status: "fullfilled", value: 2018} är ett problem, här är min föreslagna lösning:

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))

Redigera: Så låt oss säga att du ville göra ett test på din kod. Du simulerar funktionaliteten och nu vill du säkerställa att det resulterande studentYear var lika med ett förväntat värde på 2018:

Om du använder ett primitivt värde kan du bara göra så här:

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

Om du använder ett objekt:

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

När Javascript jämför ett objekt med ett annat objekt jämförs det efter dess referens, inte efter dess värden. Jag har stött på många andra huvudvärkssituationer när jag använder objekt/arrayer och försöker undvika det så mycket som möjligt. Vilket ofta inte är möjligt, och ärligt talat är det vanligtvis okej, men de tenderar att göra saker mer komplicerade än vad de annars kunde vara.

Redigera2: (Det här börjar bli lite "slarvigt")

//... 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

Redigera3: (Detta är mer i linje med vad du borde göra, men det finns ganska många olika sätt att hantera de problem som följer med asynkron kod)

getAllProperties()
  .then(useAllProperties)
  .catch((err) => console.log(err));

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