Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

sequential call using promise inside loop

Tags:

javascript

i want to perform synchronous operation of functions using promise. I have loop that passes the data to be inserted to insert function and after inserting one row i want to check the no. of rows exists in table hence i am performing select operation.

But the issue is if there are 3 records then it inserts all 3 records and after that my select function gets executed. what i want is after insertion of one record select function gets called.

here is my pseudo code as entire code involves lot of operations

for(var i=0; data.length ; i++){

 self.executeFeedbackTrack(data);

}


executeFeedbackTrack:function(callInfo){
    var self=this;
   return  self.insertFeedbackTrack(callInfo).then(function(data){

                console.log("insertFeedbackTrack status "+status);

        return self.getFeedbackTrack();

    });
},

getFeedbackTrack :function(){

    return new Promise(function(resolve,reject){
         var objDBFeedbackTrack = new DBFeedbackTrack();
        objDBFeedbackTrack.selectFeedbackTrack(function(arrayCallRegisters){
            if(arrayCallRegisters){

            console.log("notification.js no. of feedbacks "+arrayCallRegisters.length);

            resolve(arrayCallRegisters.length);

            }

        });
    });


},
insertFeedbackTrack :function(callInfo){

return new Promise(function(resolve,reject){
     var objDBFeedbackTrack = new DBFeedbackTrack();
     objDBFeedbackTrack.insertFeedbackTrack(callInfo.callNumber,callInfo.callServiceType,function(status){

             resolve(status);
            $('#loader').hide();

     });
});

}
like image 926
Hunt Avatar asked Nov 09 '17 08:11

Hunt


3 Answers

The previous answer is good, but if you are using nodejs, or babel, or you are using only modern browsers. You can use an async-await pair, it is es8 stuff.

let insertFeedbackTrack = function(){ return new Promise(/***/)};
let getFeedbackTrack = function(){ return new Promise(/***/)};
let processResult = async function(data){
   let feedbacks = [];
   for(let i=0;i<data.length;i++){
      let insertedResult = await insertFeedbackTrack(data[i]);//perhaps you will return an id;
      let feedbackTrack = await getFeedbackTrack(insertedResult.id);
      feedbacks.push(feedbackTrack);
   }
   return feedbacks;
} 

processResult(data).then(/** do stuff */)
like image 161
Alex Nikulin Avatar answered Nov 17 '22 05:11

Alex Nikulin


It looks to me like this is caused by executing a series of asynchronous inserts, and assuming that the get of insert n (inside of a .then()) is called before insert n+1 is executed. However, I'm not aware of any such guarantee, in JavaScript; all that I'm familiar with is that then n will be called after insert n, not that it would be called before insert n+1.

What I'd suggest is avoiding this mix of traditional and callback-based code, and instead put the iteration step inside getFeedbackTrack().then. Assuming this understanding of the issue is correct, then something like the following should work:

function iterate(i) {
    if (i < data.length) {
        obj.insertFeedbackTrack(data[i]).then(function(insertResult) {
            self.getFeedbackTrack().then(function(getResult) {
                // this line is the important one, replacing the `for` loop earlier
                iterate(i+1);
            });
        });
    }
}

iterate(0);

By doing that, you would guarantee that insert for the next element does not occur until the current select executes successfully.

Naturally, you may also want to restructure that to use chained .then instead of nested; I used nested rather than chained to emphasize the ordering of callbacks.

like image 44
Soron Avatar answered Nov 17 '22 03:11

Soron


This can be solved by using a very handy JS library Ramda. Concept is to use two methods, one is R.partial and another is R.pipeP.

First create a promises array from your data array, like following.

var promises = data.map(function(i) {
  return R.partial(sample, [i])
});

Then you can pass this promise to R.pipeP, so that it can be executed one after another. like below.

var doOperation = R.pipeP.apply(this, promises)

Please execute following snippet attached.

// Sample promise returning function
function sample(d) {
  return new Promise(function(resolve, reject){
    setTimeout(function() {
      console.log('resolved for:' + d);
      resolve(d);
    }, 1000)
  })
}

// Sample data
var data = [1, 2, 3, 4, 5]

// Converting data array to promise array
var promises = data.map(function(i) {
  return R.partial(sample, [i])
});

var doOperation = R.pipeP.apply(this, promises)
doOperation();
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

So in your case, the code will look like this

var promises = data.map(function(i) {
  return R.partial(self.executeFeedbackTrack, [i])
});
var doOperation = R.pipeP.apply(this, promises)
doOperation();
like image 3
Vipin Kumar Avatar answered Nov 17 '22 05:11

Vipin Kumar