Как мне переписать эту функцию, чтобы она была асинхронной?

У меня есть функция, которая извлекает изображения из API, чтобы вставить их в анимированный скроллер. Это выглядит так (упрощено для ясности):

function getPhotos(id) {
    $.when(Service.getPhotos(id))
        .then(function(results) {
            var photos = results.photos,
                scroller = $("#scroller");

            // Add the photos to the scroller
            for (var i = 0; i < photos.length; i++) {
                var photo = photos[i].url;

                // Only add a photo if its URL is valid
                if (photoExists(photo) == 200) {
                    scroller.append("<li><img src='" + photo + "' /></li>");    
                } else {
                    console.log("Photo " + photo + " doesn't exist");
                }
            }
        })
        .fail(function(err) {
            console.log(err);
        });
}

Однако URL-адреса фотографий не всегда соответствуют действительным изображениям, поэтому я запускаю их через photoExists() функция:

function photoExists(photo) {
    var http = jQuery.ajax({
        type: "HEAD",
        url: photo,
        async: false
    });

    return http.status;
}

Если заданный URL-адрес фотографии возвращает код состояния 200тогда я знаю, что изображение существует, и вставляю его в скроллер; в противном случае я пропускаю его, чтобы не вставить поврежденное изображение.

Проблема здесь в том, что async: false - потому что это не асинхронно, весь пользовательский интерфейс блокируется до тех пор, пока все не завершится, что может занять очень много времени в зависимости от того, сколько URL-адресов фотографий я должен просмотреть.

Если я использую async: trueОднако тогда photoExists() функция пытается вернуть код состояния до того, как сам запрос AJAX фактически завершится - так photoExists(photo) никогда не вернется 200, в результате чего ничего не будет добавлено в скроллер.

Как бы я настроить это так, чтобы photoExists() может работать асинхронно и, следовательно, избегать блокировки пользовательского интерфейса, но при этом возвращать правильный код состояния, чтобы я мог правильно вставить фотографии в скроллер?

1 ответ

Решение

Вы должны предоставить функцию обратного вызова для вашего photoExists функция. Что-то вроде этого:

function photoExists(photo, callback) {
    var http = jQuery.ajax({
        type: "HEAD",
        url: photo,
        async: true,
        success: function(){
            callback(photo, true);
        },
        error: function(){
            callback(photo, false);
        }
    });
}

Тогда используйте это так:

for (var i = 0; i < photos.length; i++) {
    var photo = photos[i].url;

    // Only add a photo if its URL is valid
    photoExists(photo, function(photo, isSuccessful){
        if (isSuccessful) {
            scroller.append("<li><img src='" + photo + "' /></li>");    
        } else {
            console.log("Photo " + photo + " doesn't exist");
        }
    });
}

Добавлена ​​фотография в функцию обратного вызова, чтобы избежать возможных проблем с закрытием цикла for

Другие вопросы по тегам