Mushy
Mushy

Reputation: 172

using .ajax() in .each()

I'm trying to iterate through elements of .ball class and check if their associated url exists:

$(".ball").each(function(i){

    var url;

    var c_day = $(this).data('day');
    var c_mon = $(this).data('mon');
    var c_year = $(this).data('year');

    url = c_year + "/" + c_mon + "/" + c_day + ".html";

    $.ajax({

        url: url, 
        error: function()
        {
              alert('file: ' + url + '   does not exist');
        },
        success: function()
        {
            alert('file: ' + url + 'EXXXXXXISTS!!!!!');
            blogA[ blog_count ] = url;
            blog_count++;
            $(this).css("color", "red" );           
        }
    });
 });

I've done some research and read that using .ajax in .each causes a lot of problems but I couldn't wrap my head around on how to fix it.

The problem is that I get really weird results (has to do with asynchronous?). If I alert url outside of ajax, it correctly iterates through the elements. If I alert url in ajax, it spits out urls that belong to the last element of the class.

Upvotes: 1

Views: 157

Answers (2)

vittore
vittore

Reputation: 17579

Something like this, in order to simplify your code

function successHandler(url, ball) {
  return function(ret) {
     alert('file: ' + url + 'EXXXXXXISTS!!!!!');
     ball.css('color','red')
  }
}

var balls = $('.ball'), requests = []


balls.each(function(index, ball) {
  var url = ...
  requests.push($.ajax({ url : url , success : successHandler(url, ball) })
})

$.when.apply($, requests).done(function() {
  alert('all balls are checked')
}) 

Or with ES6:

const success = (url,ball)=>(ret)=>ball.css('color','red')

const balls = $('.balls')
    , reqs = balls.map( (b, i) => {
        const url = ...
        return $.ajax({url, success:success(url,ball)})
    })

$.when.apply($, reqs).done( (resps) => alert('all done'))

A Little explanation: You are blindly using this in your callback, not knowing in which context it is going to be executed. In order to work around it and has your URL into callback we are creating function that returns a function, so it will have URL of current .ball DOM object in the context.

You'll probably also need to execute code after all ajax requests are done, so using $.when is the simplest way of doing it. And we are storing all promises just for this reason.

Upvotes: 5

jfriend00
jfriend00

Reputation: 707326

If you aren't concerned about the order of execution of each ajax call and just want to know when they are all done and the array is fully populated, then you can get this to work by fixing your reference to this and by adding a callback function that is called when all items are done:

// this function called when the ajax calls for all balls have finished
// at this point, blogA and blog_count will be populated
function ballsDone() {
    // put your code here
}

var balls = $(".ball");
var cnt = balls.length;
balls.each(function(i){

    var url;
    var self = $(this);

    var c_day = self.data('day');
    var c_mon = self.data('mon');
    var c_year = self.data('year');

    url = c_year + "/" + c_mon + "/" + c_day + ".html";

    $.ajax({

        url: url, 
        error: function()
        {
              alert('file: ' + url + '   does not exist');
              if (--cnt <= 0) {
                  ballsDone();
              }
        },
        success: function()
        {
            blogA[ blog_count ] = url;
            blog_count++;
            self.css("color", "red" );           
            if (--cnt <= 0) {
                ballsDone();
            }
        }
    });
 });

Keep in mind that the ajax calls are asynchronous so the ONLY place you can use the results from the ajax call is in the success handler. You can't use the results of the ajax calls right after the .each() call in a synchronous fashion because the ajax calls have not yet finished. You must wait for the success handler to be called and when cnt success handlers have been called, then they are all done and you can then process the results.

Upvotes: 0

Related Questions