0

I have this code:

  function lazyCss(gallery) {
    var images = gallery.slider.find('[data-lazy-image]'),
    imageLinks = [],
    imageToLoad = -1;

    for (var i = 0; i < images.length; i++) {
      imageLinks[i] = images.eq(i).data('lazy-image');
      images.eq(i).removeAttr('data-lazy-image');
    }

    function loadImage() {
      imageToLoad++;
      images.eq(imageToLoad).css('background-image', 'url(' + imageLinks[imageToLoad] + ')');
      if (imageToLoad != images.length - 1) {
        $('<img/>').attr('src', imageLinks[imageToLoad]).on('load', loadImage);
      }
    }

    loadImage();
  }

My question is:

1) When I attach an event handler "loadImage" to an image, does it consider recursion because I'm doing it inside this function?

My worries that this code will be bad for performance on my website, since recursion is considered bad.

Open up
  • 95
  • 6
  • 1
    What is shown really doesn't make sense by itself. What are you trying to accomplish? – charlietfl Jul 26 '17 at 14:27
  • _"recursion is considered bad"_ Where did you read recursion as being a "bad consideration"? No recursion occurs at JavaScript at Question – guest271314 Jul 26 '17 at 14:28
  • @charlietfl I'm trying to lazy load images on the background, so the user won't have to wait for all the images to load. I will post full code in a sec then. – Open up Jul 26 '17 at 14:31
  • @guest271314 Doesn't recursion create a chain in a memory? Like every time you call a new one, the previous one waits until the next one ends. – Open up Jul 26 '17 at 14:41
  • @Openup `load` event of `` element is asynchronous. From previous inquiries into the matter the term "recursion" is not an appropriate description of asynchronous code which calls itself again. See [What are the boundaries of recursion?](https://stackoverflow.com/questions/40499044/what-are-the-boundaries-of-recursion), [In JavaScript, what are the differences between “recursion”, “a non-terminating procedure that happens to refer to itself”, and “repeated scheduling”?](https://stackoverflow.com/questions/41292992/in-javascript-what-are-the-differences-between-recursion-a-non-terminating) – guest271314 Jul 26 '17 at 14:45
  • A "recursive" function call `return`s a value – guest271314 Jul 26 '17 at 14:57
  • @guest271314 Oh, so it's actually fine since I'm not creating a chain of calls, right? – Open up Jul 26 '17 at 14:59
  • What do you mean by "fine"? – guest271314 Jul 26 '17 at 14:59
  • @guest271314 I mean that my implementation of lazy loading images on the background won't affect performance that much. – Open up Jul 26 '17 at 15:01
  • You need to perform benchmarks and comparisons when the topic is "performance". "performance" of what versus what? – guest271314 Jul 26 '17 at 15:04

2 Answers2

1

My questions is:

1) When I attach an event handler "loadImage" to an image, does it consider recursion because I'm doing it inside this function?

No, the JavaScript at Question does not perform a "recursion". You are scheduling a possible function call, not returning a value from a function call.

See

guest271314
  • 1
  • 15
  • 104
  • 177
0

Now that we've established that your code is not recursive, let me show you a technique to make your lazy-loading occur even faster.

Right now, because of how you chain your load events, it will load each image one at a time in sequence. Using Promise.all(), you can load all of them in parallel without having to keep count of them like this:

function lazyCss(gallery) {
  var images = gallery.slider.find('[data-lazy-image]')
  var promises = images.toArray().map(img => {
    var link = img.dataset.lazyImage
    delete img.dataset.lazyImage
    img.style.backgroundImage = `url(${link})`

    return new Promise((resolve, reject) => {
      var image = new Image()
      image.onload = resolve
      // we don't want to abort or hang lazy loading if an image fails
      // so we attach resolve to onerror to prevent issues just in case
      image.onerror = resolve
      image.src = link
    })
  })

  return Promise.all(promises)
}

Now, if you have some other code that can't run until all the images are finished loading, you can call the function like this:

lazyCss(myGallery).then(() => {
  // dependent code goes here
  // won't run until all images in gallery loaded
})
Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • My goal is not just the actual performance, but also the visual one. If the user opens a page that has a slider set on autoplay, he wants most importantly the first image to be displayed the fastest, then the second one and so on. – Open up Jul 26 '17 at 16:04
  • @Openup because `map()` synchronously iterates in sequence, the prioritization is still in the same order, it just doesn't stall the rest of the images from loading. – Patrick Roberts Jul 26 '17 at 16:10
  • "it just doesn't stall the rest of the images from loading" what if the user has slow internet connection, isn't it a bad idea to load 10 last images with everything else if my current goal is to load first 5? Sorry if I ask stupid questions, I will definitely read more about promises. – Open up Jul 26 '17 at 17:01
  • @Openup that's a valid point, but if you have a minimum target amount in mind, you'd still benefit from parallelization through a technique called _batching_. Using a library like [`async`](https://caolan.github.io/async/docs.html#mapLimit), you could call `async.mapLimit(images.toArray(), 5, (img, callback) => { ... })` and it would still load the images in parallel, processing a maximum of 5 at any given time, in the same order as the array. If this idea interests you, I can add it in my answer with a more elaborate explanation. – Patrick Roberts Jul 26 '17 at 17:16