3

I am facing a problem when calling multiple post requests at the same time. I need them to be in sequence, but they get on the API in random order. How can I ensure a sequence order by using http method on JavaScript?

Here is an example of what I am doing:

for( var i = 0; i < result.length; i++ ) {
   $scope.update(result[i]);
}

$scope.update = function(results) {
    $http({
        method: 'POST',
        url: Config.serverUrl,
        data: JSON.stringify(results),
        headers: {'Content-Type': 'application/json'}
    }).then(function (response) {
        if ( response.data.status === "OK") {
            $scope.error = null;
            $scope.success = "Banco atualizado!";
        } else {
            $scope.error = response.data.message;
            $scope.success = null;
        }
        $scope.searchTimeout();
    }, function (response) {
        $scope.error = "Banco atualizado! (erro baddata)";
        $scope.success = null;
    });
};

UPDATE

Example working with two for loops and using promise as suggested by @TJCrowder:

$scope.myFunc = function(results) {
   return new Promise(function(resolve) {
      resolve($scope.update(results));
   });
};

var p = Promise.resolve();
for( var j = 0; j < result[3].length; j++ ){
    for( var i = 0; i < result[4].length; i++ ){
        p = p.then($scope.myFunc.bind($scope, results));
    }
}
afjm
  • 169
  • 12

2 Answers2

3

Add a return to your update so you return the promise, then convert the loop into a reduce call:

results.reduce(function(p, entry) {
    return p.then(function() { return $scope.update(entry); });
}, Promise.resolve());

That does the calls in series, waiting for the previous one to complete before starting the next, by creating a promise chain.

Or you can do it with just a for loop if you're doing this with a non-array list or similar:

var p = Promise.resolve();
for (var i = 0; i < results.length; ++i) {
    p = p.then($scope.update.bind($scope, results[i]));
}

We need the bind (or something similar) there because otherwise we run into the closures in loops problem. If you can rely on ES2015+ features, you can use let instead, which will let each closure close over its own i:

let p = Promise.resolve();
for (let i = 0; i < results.length; ++i) {
//   ^^^-- Important, `let` and `var` are quite different here
    p = p.then(() => $scope.update(results[i]));
}

There are more details about handling a collection of promises in series vs. in parallel in my answer to this other question, but I think it'd be a stretch to say this is a duplicate of that question.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • T.J. Crowder, could you explain how this `Promise` works? I have looked your answer but didn't get very well how would that work on the request. – afjm Oct 16 '17 at 12:02
  • @AlexandreMiziara: Do look at the "series" part of the linked answer, it goes into detail. Briefly: `reduce` calls its callback with the "accumulator" you give it as the second argument of the call to `reduce`, and then keeps doing that passing in the return value your callback provided last time. So we end up with a `Promise.resolve().then(() => $scope.update(results[0])).then(() => $scope.update(results[1])).then(...)`. So each call to `update` waits until the previous one completes. – T.J. Crowder Oct 16 '17 at 12:39
  • @TJCrowder it worked now and it is very efficient, thanks a lot! Now, why using `reduce` instead of a returnig the `Promise` on a `for` loop, for example? Because I also need to do that with a matrix and I don't know if it is possible by using reduce. – afjm Oct 16 '17 at 13:03
  • @TJCrowder, I have updated my post with an example of the array that i was sweeping with two `for` loops, if that helps. – afjm Oct 16 '17 at 13:31
  • @AlexandreMiziara: You can do it with a `for` loop as well, I've added a couple of examples. If you can't use ES2015+ stuff, `reduce` makes it a bit simpler by handling the closures-in-loops issue for you, but I've addressed that in the update. – T.J. Crowder Oct 16 '17 at 13:54
  • @TJCrowder, I saw those examples and now it worked fine too. Thanks a lot for your support. – afjm Oct 16 '17 at 15:18
  • 1
    A very comprehensive answer! "That does the calls in series, waiting for the previous one to complete before starting the next, by creating a promise chain." is exactly what I needed for submitting multiple POST requests in sequence. Thanks @T.J.Crowder! – edrichhans Aug 14 '20 at 02:56
0

you can call every POST request in success callback

like

$scope.update = function(item) {
$http({
    method: 'POST',
    url: Config.serverUrl,
    data: JSON.stringify(item),
    headers: {'Content-Type': 'application/json'}
}).then(function (response) {
    if ( response.data.status === "OK")  
         $http({ ..
}).then(function(response){
     if ( response.data.status === "OK") 
     $http({..
   }).then(function(response){
        if ( response.data.status === "OK") 
      $http({
      ..})

   })
})

  });
};
Muhammad Usman
  • 10,039
  • 22
  • 39
  • @Usman_Rana, is there a way to call the same http function on the `callback` response, instead of writing multiple functions for each response? – afjm Oct 16 '17 at 12:10
  • why do you want to call the same function multiple times in response ? if you want to call the same function more than one time, you don't need anything like that. just call it ,make sense ? – Muhammad Usman Oct 16 '17 at 12:13
  • Because this way, I would have to know how many times I want to do the requests, right? And it can ben any number of times. – afjm Oct 16 '17 at 12:21
  • I don't think if that's possible. – Muhammad Usman Oct 16 '17 at 12:23