简体   繁体   中英

Potential errors in asynchronous loading of images in photo viewer (Javascript and jQuery)

I'm going through a book called "Javascript & jQuery" by Jon Duckett. In it, there's an example of a photo viewer that can be found here:

http://javascriptbook.com/code/c11/photo-viewer.html

In the book, he states that

because larger images take longer to load, if a user clicks on a two different images in quick succession: 1) The second image could load faster than the first one and be displayed in the browser. 2) It would be replaced by the first image the user clicked on when that image had loaded. This could make users think the wrong image has loaded.

I get this concept, however, the code he cites does not seem to fix the problem, at least not in my eyes. Here is an abbreviated version of the full script, which can be found here: http://javascriptbook.com/code/c11/js/photo-viewer.js (his version is much better commented)

var request;
var $current;
var cache = {};
var $frame = $('#photo-viewer');
var $thumbs = $('.thumb');

// Other code goes here...

$(document).on('click', '.thumb', function(e){    // User clicks on thumbnail
    var $img;
    var src = this.href;
    request = src;

// Other code goes here...

    $img = $('<img/>');
    cache[src] = {
        $img: $img,
        isLoading: true
    };

    $img.on('load', function() {                     // Code to run when image loads
        $img.hide();
        $frame.removeClass('is-loading').append($img);
        cache[src].isLoading = false;
        if (request === src) {                       // Check to make sure the image that is loading is the most recently requested image
            crossfade($img);                         // Call function to load new image
        }
    });

    $img.attr({
        'src': src,
        'alt': this.title || ''
    });

So basically his solution is to create a global "request" variable and a local "src" variable. When a user clicks on a thumbnail, the request variable is assigned the value of src, which is the path to the image being requested. Later on, when the image loads, that request variable is checked to ensure that it equals src.

The logic behind the line that performs the check seems faulty to me. However, I'm new to programming and I'm not sure if there's a concept I'm not fully understanding. The line in question is this one:

if (request === src) {

Since the request variable was assigned the value of the src variable earlier in the script, they would still be the same, even when an old image loads after the new one has been clicked. The only difference is that value of each would be different.

Wouldn't the following be the right way to check this?

if (request === this.src) {

That way if the old image loads after the most recently requested image, the request variable would have stored the most recent path, and the old image's src attribute would no longer match it.

This might be pretty confusing, since I didn't include the whole script, so let me know if you need any elaboration. I tried to only include the absolutely critical aspects of the code so that you wouldn't have to go through all of it to understand my question.

Thank you for any help!

The code in the book is correct. The trick here is to have a variable across all instances of the callback that contains the most recently clicked href.

|---1----------...loading...------------cb------------|

|---2--...loading...--cb------------------------------|

|---3-----...loading...-----------cb------------------|

If this were the timeline of three clicks...we click a large image first (longer loading time before callback (cb)) and then we click a second, smaller image, finally followed by a medium image. We would have three click events all firing at the same time. The first callback we'd get is the 2nd click, followed by the third click, followed by the first click. However, since our request variable is outside the scope of our callback, it's value will be equal to the last thing it was set to, in this case, the href of click three.

I think all this logic makes sense to you but it's the timing you're not understanding. When click one happens, we set the external variable request to href1 , when click two happens, we set it to href2 and then finally click three sets it to href3 . These things all happened prior to the .on('load') callback which means, when image two loads first and we check the value of request , it's already been set to href3 which is !== src (href2), once image 3 loads next in the timeline, we see that this is the image we expect so we load it. Some few odd seconds/minutes later when image one calls it's callback, we see that request is not the href for image 1 and we don't show image one.

Either your code or the suggested code should work. The key you are missing is the scopes of the two variables.

var request;

$(document).on('click', '.thumb', function(e){    // User clicks on thumbnail
    var $img;
    var src = this.href; //variable exists only inside this onclick callback
    request = src;

request exists outside of the enclosure so every time an image is clicked, its value gets overridden and stored.

src is declared inside of the onclick callback function and initialized with the link href. If another image is clicked while the first is loading, a new src variable is created to store the new data. As far as the onload callback is concerned, src doesn't change at that scope. src remains the value that it was first set at (the href of the link) and isn't overridden by following clicks.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM