1

I have a page with a carousel which will send an ajax request each time a slide has changed, and will generate products related the slide into another carousel at the bottom.

At the moment when each slide has changed, the products are successfully drawn with Ajax, though I need to initiate the slider with the products once the ajax request has loaded. Right now the slider tries to initialize before the requests have finished.

On the bottom of the code I added, the each function adds each of the getProducts function to an array and then when it is done, it should initialize the slider. Though in the console the message 'this is initialized' happens before the 'success' messages in the Ajax request.

Have I used the deferred wrong in this example to cause this problem?

        var products = [],
        uniqueProducts = [], 
        defs = [];           
        var el;
        $('.your-class [data-slick-index="' + currentSlide + '"] a').each(function(i) { 
              el = $(this).attr("href");      
              products.push(el);
              $.each(products, function(j, el) {
                  if ($.inArray(el, uniqueProducts) === -1)
                      uniqueProducts.push(el);
                      console.log("pushed" + uniqueProducts);
              });
        });
        function getProducts(el) {
          var def = new $.Deferred(); 
          var url = el;

          $.get(url, function(data) {  
                var imageArray = data.match(/<img itemprop="image" [\S\s]*?>/ig);
                var $image = $(imageArray[0]);
                var imageUrl = $image.attr('src');
                var name = $image.attr('title');
                var priceArray = data.match(/<p class="price">[\S\s]*?<\/p>/ig);
                var priceEl = $(priceArray[0]).find('[itemprop=price]');
                priceEl.children().remove();
                var price = priceEl.text() ? '$' + priceEl.text() : '';
                $( ".carousel2" ).append( '<div><img src=\" '+ imageUrl +'\"></div>');
                console.log("success");
                def.resolve();
          });
          return def.promise();
        }

      $.each(uniqueProducts, function(i, el) {
          defs.push(getProducts(el));
      });

      $.when($,defs).done(function () {
          $('.carousel2').slick({ speed: 500, autoplay: false, autoplaySpeed: 4000, arrows:false });
          console.log("this is initialized");
      });           
    } 
Evan Carslake
  • 2,267
  • 15
  • 38
  • 56
detas12
  • 25
  • 3
  • Needed to add apply after the '$.when' in the last function $.when.apply($,defs).done(function () { $('.carousel2').slick({ speed: 500, autoplay: false, autoplaySpeed: 4000, arrows:false }); console.log("this is initialized"); }); – detas12 Aug 28 '15 at 00:51

2 Answers2

0

I haven't played with $.when for a while but I think you could maybe get this working without having to create $.Deferred() instances as $.get will return this for you.

Possibly try having getProducts return the $.get instead of def.promise and take out any reference to def?

Hope that can help you out!

p.s I hunted out some old code where I used this $.when to see how I used it with $.get. I've simplified it and something along the lines of the following should work.

$.when([
  $.get("data/a.json"),
  $.get("data/b.json"),
  $.get("data/c.json")
]).done(function (t1, t2, t3) {
  app.a = t1[0];
  app.b = t2[0];
  app.c = t3[0];
});
Jhey
  • 1,377
  • 7
  • 10
  • Thanks for the help, it looked like I was missing a '.apply' after the $.when in the last function though.When I can, i'll try your solution to see if that works too – detas12 Aug 28 '15 at 00:47
  • No problem :) happy to help I managed to find an example of where I've used it before and have appended that to my answer. You can just pass in an array of `$.get` requests it seems so should work fine. – Jhey Aug 28 '15 at 00:52
0

With credit to this answer, building uniqueProducts will simplify to two one-liners.

var uniqueProducts = $('.your-class [data-slick-index="' + currentSlide + '"] a').map(function(el) {
    return $(el).attr('href');
}).get().filter(function(href, pos, self) {
    return self.indexOf(href) == pos;
});

And getProducts() should simplify as follows :

function getProducts(url) {
    return $.get(url).then(function(data) {
        var image = $(data.match(/<img itemprop="image" [\S\s]*?>/ig)[0]);
        var price = $(data.match(/<p class="price">[\S\s]*?<\/p>/ig)[0]).find('[itemprop=price]').children().remove().end().text();
        return {
            name: image.attr('title'),
            image: image,
            price: price ? '$' + price : ''
        };
    });
}

Note that getProducts() now has no side effects but returns a data object.

Then by using uniqueProducts.reduce(...), you can call getProducts() and process the data delivered by the promises.

Assuming everything takes place in a function, you will end up with something like this :

function initializeCarousel() {
    return $('.your-class [data-slick-index="' + currentSlide + '"] a')
    .map(function(el) {
        return el.href;
    })
    .get()
    .filter(function(href, pos, self) {
        return self.indexOf(href) == pos;
    })
    .reduce(function(sequence, url) {
        var productPromise = getProducts(url);
        return sequence
        .then(function() {
            return productPromise;
        })
        .then(function(dataObj) {
            $(".carousel2").append(dataObj.image);
            // ... dataObj.name ...
            // ... dataObj.price ...
        }, function() {
            return sequence;//skip over error
        });
    }, $.when())//resolved starter promise for the reduction
    .then(function () {
        $('.carousel2').slick({ speed: 500, autoplay: false, autoplaySpeed: 4000, arrows:false });
        console.log("this is initialized");
    });
}

Features of this particular .reduce pattern are :

  • ajax calls are made in parallel.
  • very simply converted serial calls, if required.
  • the order of images appended to the carousel will be congruent with the reduced array, ie the "right order".
  • any individual ajax error does not scupper the whole enterprise.
  • no need for the intermediate promises array or for jQuery's cumbersome $.when.apply(null, promises) (or the more friendly .all() in other libs).
Community
  • 1
  • 1
Roamer-1888
  • 19,138
  • 5
  • 33
  • 44