DigitalMC
DigitalMC

Reputation: 877

Stopping Code until AJAX call completes inside $.each loop

I have a function that loads HTML from an external file via an AJAX call using jQuery.

These ajax call runs inside of a $.each loop and I need this ajax call to finish before the loop continues. Here is my code:

$('img').each(function(){
    var cotainer_html = $('.cotainer_html').clone();
    /* Get Image Content */
    $.ajax({
       url: '/assets/ajax/get_studio_image.php',
       type:'GET',
       success:function(data){
          cotainer_html.find('.replaceme').replaceWith(data);
       }
    });
});

I know I can set async:false but I hear that is not a good idea. Any ideas?

Upvotes: 0

Views: 576

Answers (2)

Alnitak
Alnitak

Reputation: 339816

You can use a pseudo-recursive loop:

var imgs = $('img').get();

var done = (function loop() {
    var img = imgs.shift();
    if (img) {
        var cotainer_html = $('.cotainer_html').clone();
        /* Get Image Content */
        return $.get('/assets/ajax/get_studio_image.php')
         .then(function(data) {
            cotainer_html.find('.replaceme').replaceWith(data);
        }).then(loop);
   } else {
       return $.Deferred().resolve();  // resolved when the loop terminates
   }
})();

This will take each element of the list, get the required image, and .then() start over until there's nothing left.

The immediately invoked function expression itself returns a Promise, so you can chain a .then() call to that that'll be invoked once the loop has completed:

done.then(function() {
    // continue your execution here 
    ...
});

Upvotes: 2

Rory McCrossan
Rory McCrossan

Reputation: 337560

To achieve this you can put each request in to an array and apply() that array to $.when. Try this:

var requests = [];
$('img').each(function(){
    var cotainer_html = $('.cotainer_html').clone();
    /* Get Image Content */
    requests.push($.ajax({
       url: '/assets/ajax/get_studio_image.php',
       type:'GET',
       success:function(data){
          cotainer_html.find('.replaceme').replaceWith(data);
       }
    }));
});

$.when.apply($, requests).done(function() {
    console.log('all requests complete');
});

Note that you're replacing the same content on each request, so the only one which will have any effect on the UI is the last request. The preceding ones are redundant.

Also note that you should never, ever use async: false. It locks the UI thread of the browser until the request completes, which makes it look like it has crashed to the user. It is terrible practice. Use callbacks.

The OP appears to want the calls to run in series, not in parallel

If this is the case you could use recursion:

function makeRequest($els, index) {
    var cotainer_html = $('.cotainer_html').clone();
    $.ajax({
       url: '/assets/ajax/get_studio_image.php',
       type:'GET',
       success:function(data){
          cotainer_html.find('.replaceme').replaceWith(data);
          if ($els.eq(index + 1).length) {
              makeRequest($els, ++index);
          } else {
              console.log('all requests complete');
          }
       }
    });
}

makeRequest($('img'), 0);

Upvotes: 2

Related Questions