0

I am handling multiple nested calls by using jQuery's $.Deferred function. Currently the code is not working properly. I want to fire a function when all requests are done. but currently after two requests it reaches the console.log.

function ajac(){
  var dfd = $.Deferred();
  var api = require('modules/api');
  for(var i=0;i<2;i++){
      api.request("GET","https://t25501-s39032.sandbox.mozu.com/events/priceadjustment").then(function(res) {
          api.request("GET","https://t25501-s39032.sandbox.mozu.com/events/priceadjustment").then(function(res) {
            dfd.resolve();
          });
      });
  }
  return dfd.promise();
}
$.when(ajac()).then(function(){
  console.log("reached");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Alexander Nied
  • 12,804
  • 4
  • 25
  • 45
Ajay Thakur
  • 1,066
  • 7
  • 23

3 Answers3

0

The problem you are having is that you are ultimately defining, resolving and returning a single deferred, even though your loop indicates that you are intending to track multiple deferreds. $.when expects multiple deferreds to be passed as individual arguments. In order for the example to work, I included a mock asyncEvent function pulled from the jQuery documentation. See the below snippet to see everything working as expected:

function asyncEvent() {
  var dfd = jQuery.Deferred();
 
  // Resolve after a random interval
  setTimeout(function() {
    dfd.resolve( "hurray" );
  }, Math.floor( 400 + Math.random() * 2000 ) );
   
  // Return the Promise so caller can't change the Deferred
  return dfd.promise();
}

function makeAjac(i) {
    var dfd = $.Deferred()
    asyncEvent().then(function(res) {
        asyncEvent().then(function(res) {
          console.log(`request pair ${i} complete.`);
          dfd.resolve();
        });
    });
    return dfd.promise();
}

function ajac() {
  var promisesArray = [];
  for(var i=0;i<2;i++){
    promisesArray.push(makeAjac(i));
  }
  return promisesArray;
}

$.when.apply($, ajac()).then(function(){
  console.log("reached");
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

What we've done here is pull the code that was executing inside your for loop into its own function makeAjac which returns the promise for its particular iteration through the loop. In the original ajac then we push those returned promises into our promisesArray, which we in turn return from ajac. Finally, we leverage .apply in order to pass the promises in the array as separate arguments to $.when. As you can see from running the snippet, this functions as expected.

Alexander Nied
  • 12,804
  • 4
  • 25
  • 45
  • @DavidBray - That certainly may be true-- I was simply trying to answer the user's specific question regarding the jQuery deferred object. Given the frequent use of `var` and this being a jQuery-specific question, it is possible the OP is in a scenario in which they need to support browsers older browsers that [don't support Promises, like IE](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility). – Alexander Nied Jan 08 '19 at 15:01
-1

A single promise can be resolved or rejected only once. You need to return different promises to have all the promises resolved. Something like this:

function ajac(){
  var allPromises = [];
  var api = require('modules/api');
  for(let i=0;i<2;i++){
      let dfd = $.Deferred();
      api.request("GET","https://t25501-s39032.sandbox.mozu.com/events/priceadjustment").then(function(res) {
          api.request("GET","https://t25501-s39032.sandbox.mozu.com/events/priceadjustment").then(function(res) {
            dfd.resolve();
          });
      });
      allPromises.push(dfd.promise());
  }
  return Promise.all(allPromises);
}
$.when(ajac()).then(function(){
  console.log("reached");
})

A better way to do this would be to use Observables (Example: RxJS)

You can refer to this answer to understand the diffference between pomises and observables: https://stackoverflow.com/a/37365955/6080889

dRoyson
  • 1,466
  • 3
  • 14
  • 25
  • 1
    Unfortunately, I don't _believe_ the above snippet will work. Because a _single_ deferred is used for both iterations through the loop (`dfd`), both promises in the `allPromises` array will never reach a "resolved" state and thus the `Promise.all` will not resolve. I could be wrong, but I tried pulling this into a snippet with some modified mock async functions and that appeared to be the case. – Alexander Nied Jan 08 '19 at 06:09
  • @AlexanderNied thanks for pointing that out. It was a scope issue using `var`. I have updated my answer to initialize `dfd` using `let`. – dRoyson Jan 09 '19 at 04:24
-1

You can try this:

You need to go through with this link: Using Multiple Deferred Promises - JavaScript/jQuery

var callAPI=function(){
const promise = new Promise((resolve, reject) => {
         var api = require('modules/api');
         resolve(api.request("GET","https://t25501-s39032.sandbox.mozu.com/events/priceadjustment"));
  });
return promise;
}    


function getInfo(){
    const promise = new Promise((resolve, reject) => {
            var allPromise=[];
            for(var i=0;i<2;i++){
                  allPromises.push(callAPI());
            }
                  return Promise.all(allPromises);
     });
return promise;
}

getInfo().then((result)=>{
       console.log("reached",result);
});
Shubham Verma
  • 8,783
  • 6
  • 58
  • 79