2015-06-20 4 views
3

Following this fiddle из this question, я написал этот кусок кода:Проблема возвращения ответа от асинхронного вызова

var currentSlideCount = window.imgIds.length; 

for (var i = 11; i < (currentSlideCount + 10); i++) { 

// SET UP NEW SLIDE HTML 
    var newSlide = '<li><img id="apod' + i + '" class="rounded-corners apod-image"></li>'; 
    $('#lightSlider').append(newSlide); 
    window.imgIds.push('apod'+i); 
    console.log(window.imgIds); 

// GENERATE DATE 
    var date = new Date(); 
    date.setDate(date.getDate() - i); 
    var day = date.getDate(); 
    var month = date.getMonth(); 
    var year = date.getFullYear(); 
    console.log(year + "-" + month + "-" + day); 

// GENERATE XML REQUEST 
    function foo(callback) { 
     var apodUrl = "https://api.nasa.gov/planetary/apod?concept_tags=True&date=" + year + "-" + month + "-" + day; 
     var apodXml = new XMLHttpRequest(); 
     apodXml.open('GET', apodUrl, true); 
     apodXml.send(null); 

     // WHEN REQUEST IS READY 
     apodXml.onreadystatechange=function() { 
      if (apodXml.readyState==4 && apodXml.status==200) { 
       var apodParse = JSON.parse(apodXml.responseText); 
       callback(apodParse.url) 
       console.log(apodParse.url); 
      } 
     } 
    } 

    foo(function(result) { 
     var newSlideId = 'apod' + i; 
     document.getElementById(newSlideId).src = result; 
    }); 

Но я все еще получаю Невозможно установить свойство «SRC» в нуль ошибки консоли на IMG тег который был создан задолго до того, как будет вызван атрибут src. И насколько я понимаю, я правильно настроил обратный вызов. Почему это все еще не работает?

ответ

0

Две проблемы там:

  1. вы используете объявление функции внутри структуры управления. Вы не можете этого сделать, это недействительно; некоторые браузеры попытаются переписать его для вас как выражение функции, но другие не будут. Объявления функций действительны только на верхнем уровне области, вне всех структур управления. Например, в глобальном масштабе или на верхнем уровне функции.

  2. Что еще более важно, обратный вызов вы передаете к foo имеет стойкую ссылку к i переменным, а не копии о нем как при создании функции. Таким образом, все они видят i, как это происходит, когда они запускаются позже на конце конца цикла.

Переместите foo функцию из структуры управления, а в идеале параметрирование, в частности, передавая ему значение i, что обратный вызов должен использовать. Например .:

var currentSlideCount = window.imgIds.length; 

for (var i = 11; i < (currentSlideCount + 10); i++) { 

    // SET UP NEW SLIDE HTML 
    var newSlide = '<li><img id="apod' + i + '" class="rounded-corners apod-image"></li>'; 
    $('#lightSlider').append(newSlide); 
    window.imgIds.push('apod' + i); 
    console.log(window.imgIds); 

    // GENERATE DATE 
    var date = new Date(); 
    date.setDate(date.getDate() - i); 
    var day = date.getDate(); 
    var month = date.getMonth(); 
    var year = date.getFullYear(); 
    console.log(year + "-" + month + "-" + day); 

    foo(year, month, day, i, function(result, index) { 
     var newSlideId = 'apod' + index; 
     document.getElementById(newSlideId).src = result; 
    }); 
} 

// GENERATE XML REQUEST 
function foo(year, month, day, index, callback) { 
    var apodUrl = "https://api.nasa.gov/planetary/apod?concept_tags=True&date=" + year + "-" + month + "-" + day; 
    var apodXml = new XMLHttpRequest(); 
    apodXml.open('GET', apodUrl, true); 
    apodXml.send(null); 

    // WHEN REQUEST IS READY 
    apodXml.onreadystatechange = function() { 
     if (apodXml.readyState == 4 && apodXml.status == 200) { 
      var apodParse = JSON.parse(apodXml.responseText); 
      callback(apodParse.url, index) 
      console.log(apodParse.url, index); 
     } 
    } 
} 
3

Во-первых, вы объявляете функцию foo в цикле. Хотя это не приводит к ошибке, это плохая практика. Функция должна быть объявлена ​​вне цикла.

Во-вторых, функция обратного вызова, переданная в foo, вызывается асинхронно (т. Е. Через AJAX). Переменной i присваивается значение в области родительской области функции обратного вызова и в цикле. Цикл завершит выполнение к моменту вызова обратного вызова. Когда вызывается обратный вызов, он будет выглядеть до цепочки областей, чтобы найти значение i, и оно найдет i, объявленное в цикле. i будет равен окончательному значению в цикле, для которого условие цикла i < (currentSlideCount + 10) оценивает значение false и не продолжается.

Хотя это может быть трудно следовать, вы можете увидеть, что я имею в виду, добавив alert(i); функции обратного вызова:

foo(function(result) { 
    alert(i); 
    var newSlideId = 'apod' + i; 
    document.getElementById(newSlideId).src = result; 
}); 

Вы можете быть удивлены тем, что тревога всегда будет показывать то же значение для i ,

Чтобы решить эту проблему, вам необходимо использовать сразу выполняющуюся функцию для создания новой области, где i передается по значению в качестве нужного значения.

Изменить это:

foo(function(result) { 
    var newSlideId = 'apod' + i; 
    document.getElementById(newSlideId).src = result; 
}); 

Для этого:

foo(
    (function(i) { 
     return function(result) { 
      var newSlideId = 'apod' + i; 
      document.getElementById(newSlideId).src = result; 
     } 
    })(i) 
); 

В JavaScript область видимости разграничены на уровне функции. Используя сразу исполняемую функцию, вы добавляете новую область, где i передано по значению для текущей итерации цикла.

Переменный обзор в JavaScript может быть сложным для понимания, и ваш вопрос попадает прямо на один из более сложных сценариев. Возможно, вам будет полезно ознакомиться с обзором other explanations с описанием области JavaScript.

+0

Да, это имеет смысл. Спасибо, что объяснили. Сфера охвата была одной из самых неприятных вещей в обучении js. На этой же заметке вы можете объяснить, как я объявляю функцию НЕ внутри цикла? Неужели это вызовет другие проблемы? –

+0

@JohnDoe Вы бы просто отрезали 'function foo {....}' часть вашего кода и вставляли ее в начало образца вашего кода. Объем функции не изменился. Область видимости определяется на уровне функции, поэтому перемещение ее за пределы цикла не имеет никакого отношения к сфере видимости. Прямо сейчас, с функцией, объявленной внутри цикла, вы повторяете одну и ту же функцию снова и снова с каждой итерацией цикла - это не хорошо для производительности вашего кода. –

Смежные вопросы