1

I have an ajax query followed by some functions, and I use the .then() promise callback to execute them in order:

var pictures = [];
var venues = **an array of venues**

$.get(url).then(functionA, functionFail).then(function B);

But functionA, the first success callback, includes a loop that fires off 'n' ajax requests:

for(var i=0; i<n; i++) { 
    var venue = venues[i];
    var new_url = **some_url**

    $.ajax({url: new_url, async: false}).done(function(data) {
        var pics = data.response.photos.items;
        pictures.push(pics[0]);
        pictures.push(pics[1]);
      }).fail(function() {
      console.log('Failed!');
    });
}

These looped ajax requests fill up the global pictures array. The pictures array is then used by functionB, but because of the async nature, the array doesn't get filled up fully and executes right away.

I tried to make the requests synchronous with async: false but it's not completely effective (it leaves out the very last request of the loop).

How can I make sure that functionB is only executed after all the ajax requests have finished? I don't want to use timeouts but if nothing else I'll fall back on that.

zeo
  • 68
  • 10
  • maybe add a counter that counts up on every request success and executes functionB if the counter reaches n? – yadejo Jul 28 '17 at 14:47
  • @yadejo that wouldn't work here. :) – zeo Jul 28 '17 at 14:50
  • [Here's my answer to a similar question](https://stackoverflow.com/a/22452909/1377002) from a while back that might help you. It uses an array of URLs each of which is used in a promise all of which are passed into `$.when`. – Andy Jul 28 '17 at 14:51
  • @Andy Trying that approach right now after one of the answers suggested it. Thanks! – zeo Jul 28 '17 at 14:54

4 Answers4

1

As you're using jQuery, it looks like jQuery.when() can take multiple results and then call done once they're all resolved.

Axnyff
  • 9,213
  • 4
  • 33
  • 37
  • This sounds promising. I'll have to restructure my code a little, let me try this! :) – zeo Jul 28 '17 at 14:53
  • @Andy @Axnyff Ah, I tried it but it's not the best approach for me. :) The problem is that this approach requires you to know exactly how many requests you are making (for passing arguments to `$.when()`). The number of requests is variable in my case and it doesn't allow me to pass in an array of ajax requests objects (which might have worked!). So I'll just be using synchronous requests and a timeout for now, although it's a workaround and not very elegant. :/ – zeo Jul 28 '17 at 15:06
  • Can't you create an array of all the promises and then jQuery.when.call(jQuery, myArray) ? https://stackoverflow.com/questions/5627284/pass-in-an-array-of-deferreds-to-when – Axnyff Jul 28 '17 at 15:32
  • 1
    Hey thanks! Just wanted to let you know that I found Kermit's suggestion to fit my needs the most. :) I'm now using Promise.all() and it is now very simple. https://stackoverflow.com/a/45377061/5533689 – zeo Jul 30 '17 at 18:27
1

Not sure if this is the best answer, but one of them! Just count the number of times request is completed, and when all of them are, execute your function.

var completed_requests = 0,
    returned_data = [];

for (var i=0; i<n; i++) { 
    var venue = venues[i];
    var new_url = **some_url**
    var done = function(data_array) {
        var pics = [];
        data_array.forEach(function(data) {
            pics = pics.concat(data.response.photos.items);
        });
      };

    $.ajax({url: new_url}).done(function(data) {
        completed_requests++;
        returned_data.push(data);
        if (completed_requests == n) {
            done(returned_data);
        }
    }).fail(function() {
      console.log('Failed!');
    });
}

My example also saves data from all requests until you need it.

Slava Eremenko
  • 2,235
  • 1
  • 11
  • 9
  • I tried that already, it's intuitive but didn't work very well. :) With this approach I'll need to have function B 'listen' for the variable `completed_requests` to reach a certain value, and till then the browser can freeze in some cases! – zeo Jul 28 '17 at 14:59
  • You don't need to 'listen' to it! Only check the value when request is completed, because that's the only time when completed_requests value is changed. And the browser won't freeze. – Slava Eremenko Jul 28 '17 at 15:00
  • Oh I see you modified the code a little - I'll try calling done() from within the function with new data. Interesting trick! – zeo Jul 28 '17 at 15:14
0

You can handle the looping yourself, handling only one item in venues at a time. Then upon the the completion of one, call a function to handle the next one, and when venues is empty then call your functionB

ar pictures = [];
var venues = **an array of venues**

$.get(url).then(functionA, functionFail);

function functionA() {
  var venue = venues.shift();
  var new_url = **some_url**;

    $.ajax({
            url: new_url, 
            async: true
          }).done(function(data) {
            var pics = data.response.photos.items;
            pictures.push(pics[0]);
            pictures.push(pics[1]);
            if(venues.length !== 0) {
                functionA();
            }
            else {
                functionB();
            }
          }).fail(function() {
          console.log('Failed!');
    });
}
CumminUp07
  • 1,936
  • 1
  • 8
  • 21
0

you can use Promise.all, code of function A:

var requests = []
for(var i=0; i<n; i++) { 
    var venue = venues[i];
    var new_url = **some_url**
    request.push(new Promise((resolve, reject) => {
      $.ajax({url: new_url}).done(function(data) {
          var pics = data.response.photos.items;
          resolve(pics)
        }).fail(function(err) {
        reject(err)
        console.log('Failed!');
      });
    }))
}

return Promise.all(requests)

when all request are run successful, the whole return values of requests will be pushed to array and passed to function B.

Kermit
  • 1,062
  • 6
  • 10