Adam Harkus
Adam Harkus

Reputation: 2210

SAPUI5 processing a loop with promises

oModel.create("/SurveySet", oEntry, { 
    success: function(oData) { 
        for (var i = 0; i < questionData.questions.length; i++) {
            var oEntry = {};
            oEntry.SurveyId = oData.SurveyId; 

            oModel.create("/QuestionSet", oEntry, { 
                changeSetId: i.toString(), 
                success: function(oData) {
                    //Additional Processing

                }
           }
       }
   }
}

I'm Creating a SurveySet which returns a SurveyId.

For each Survey I need to create a QuestionSet in a FOR loop, returning the Data each time for additional processing.

The issue is in the order of execution. The for loop is incremented BEFORE the oData is retrieved from the 1st iteration.

E.g. if the for loop has 0 and 1, only the last element 1 is executed.

How do I delay the incrementation of the for loop until AFTER the oData is returned for the first iteratiion of the loop?

Upvotes: 1

Views: 4970

Answers (2)

schnoedel
schnoedel

Reputation: 3948

I guess you access oEntry or i in the "Additional Processing".

The problem is that the inner success function with the additional processing is capturing the local variables defined outside like i and oEntry in a closure. The variables values are not copied.

  1. The for loop increments i, changes oEntry and executes the oModel.create() method.
  2. Next loop: The for loop increments i again, changes oEntry and executes oModel.create() again.
  3. When the loop is done or any time later after the request to the backend has completed, your inner success handlers will be called. They access the outer variables which have only survived that long because they were captured in the closure. And they will be in the state they were when the for loop has finished.

So if you don't mind that your additional processing might happen out of order you can move the code inside the for loop into a separate function. When you call that function from the for loop the variable values will be copied so that each before mentioned closure will capture the copies which will not be changed by the for loop:

createSurvey: function(oEntry){
   var that = this;
   oModel.create("/SurveySet", oEntry, { 
       success: function(oData) { 
           for (var i = 0; i < questionData.questions.length; i++) {
               that.createQuestion(oModel, questionData.questions, i,  oData);
          }
      }
   } 
}
createQuestion(oModel, questions, i, survey){
            var oEntry = {};
            oEntry.SurveyId = survey.SurveyId; 

            oModel.create("/QuestionSet", oEntry, { 
                changeSetId: i.toString(), 
                success: function(oData) {
                    //Additional Processing

                }
           }

}

PS: You can also use questionData.questions.forEach(function(question, i){ ... }); to get the same effect. This time the anonymous function copies the values.


If you need to maintain strict order for your additional processing and send the requests sequencially to the backend, i would indeed recommend the use of promises:

createSurvey: function(oEntry){
   var that = this;
   oModel.create("/SurveySet", oEntry, { 
       success: function(oData) { 
           var promise = Promise.resolve();
           questionData.questions.forEach(function(question, i) { //copy local variables
               //Chain the promises
               promise = promise.then(function(){ return that.createQuestion(oModel, question, i, oData)});
           });
           promise.then(function(){ 
               //All done
           })
           .catch(function(){
               //Error somewhere. remaining not executed
           })
      }
   } 
}
createQuestion(oModel, question, i, survey){ //returns a Promise now
    var oEntry = {};
    oEntry.SurveyId = survey.SurveyId; 

    return new Promise(function(resolve,reject){ //Wrap the UI5 stuff into a Promise
        oModel.create("/QuestionSet", oEntry, { 
            changeSetId: i.toString(), 
            success: function(oData) {
                //Additional Processing
                resolve(oData);
            },
            error: reject
        }
    });
}

Upvotes: 3

Diogo Fernandes
Diogo Fernandes

Reputation: 134

You can push your Survey data to an array,then do the FOR loop

var data = [];

oModel.create("/SurveySet", oEntry, { 
       success: function(oData) { 
       data.push(oData.SurveyId);
       }
}

$.each(data, function(index, value) {

       for (var i = 0; i < questionData.questions.length; i++) {
            var oEntry = {};
            oEntry.SurveyId = value.SurveyId; 

            oModel.create("/QuestionSet", oEntry, { 
                changeSetId: i.toString(), 
                success: function(oData) {
                    //Additional Processing

                }
           }
       }

)};

Upvotes: 1

Related Questions