How to due with Promise inside Promise


#1

Please help me to due with Promise inside Promise!
My PROVIDERS function do:

  1. Get Questions from DBCALCULATOR (Promise)

  2. With each Question, I checked if it is a multiple_choice question

  3. If it is a multiple_choice question, call a SQL to get choices from DBANSWER_CHOICE (Promise again) and push all results to that question.
    If not, push that question to formulars[]

But the problem is the getQuestions() function is not return a final formulars[], it’s always return undefined

getQuestions(calculatorIDENTIFIER): Observable<any>{
    let sqlQueryQuestion = `SELECT
                      *
                    FROM
                      DBQUESTION
                    WHERE
                      CALCULATOR_ID = (
                        SELECT
                          _id
                        FROM
                          DBCALCULATOR
                        WHERE
                          IDENTIFIER = '${calculatorIDENTIFIER}'
                      )
                    ORDER BY
                      ORDERED_ID ASC;`
    let sqlQueryAnswerChoice = `
    SELECT * FROM DBANSWER_CHOICE WHERE QUESTION_ID=_QUESTION_ID_
    `
    let formulars=[];

    let db = this.database
    return Observable.fromPromise(
      db.executeSql(sqlQueryQuestion, {}).then(re=>{
      if(re.rows.length>0){
        let cInd = -1
        let checkTungCauHoi = function(){
          cInd++
          if (cInd < re.rows.length) {
            let cauHoiNay = re.rows.item(cInd)
            /* get AnswerChoice */
            if( cauHoiNay.TYPE == "multiple_choice" ){
              db.executeSql(sqlQueryAnswerChoice.replace(/_QUESTION_ID_/, cauHoiNay._id), {}).then(re2=>{
                if(re2.rows.length>0){
                  cauHoiNay.choices=[]
                  for (var ind2 = 0; ind2 < re2.rows.length; ind2++) {
                    cauHoiNay.choices.push(re2.rows.item(ind2))
                  }
                  formulars.push(cauHoiNay)
                  checkTungCauHoi()
                } 
              })
            /* Else not multiple_choice thì push và gọi hàm tiếp */
            } else {
              formulars.push(cauHoiNay)
              checkTungCauHoi()
            }
          } else {
            return formulars
          }
        }
        checkTungCauHoi()
      }
    })
  )
  }

#2

I think this function is far too complex and should be broken up, which should clear up the problem.

I can’t do this as precisely as I would like because you have used SQL’s * and any, both of which make your data structure impossible for a reader to understand, so I’m going to have to make it up and you’re going to have to adapt it.

export interface Question {
  id: string;
  flavor: 'multiple' | 'free';
  prompt: string;
  choices?: string[];
}

getQuestions(query: string): Promise<Question[]> {
  return this.db.executeSql(query).then(dbrv => {
    let qps: Promise<Question>[] = [];
    for (let rix = 0; rix < dbrv.rows.length; ++rix) {
      let chn = dbrv.rows.item(rix);
      let q: Question = {id: chn.id, flavor: chn.flavor, prompt: chn.prompt};
      let qp: Promise<Question>;
      if (chn.flavor === 'multiple') {
        qp = this.getMultipleChoices(q.id).then((choices) => {
          q.choices = choices;
          return q; 
        });
      } else {
        qp = Promise.resolve(q);
      }
      qps.push(qp);
    }
    return Promise.all(qps);
  });
}

Presumably you can handle getMultipleChoices(), which returns a Promise<string[]> of the choices for a given question id.