Глобальный массив не обновляется после .push внутри функции

Эй, ребята, кто может мне помочь? По сути, я хочу сделать нижний колонтитул с последними новостями, который перебирает массив newsWire и автоматически обновляет текст. Проблема в том, что когда я запускаю свой console.log(newsWire.length) вне функции loadNewswire, он возвращает 0, а console.log внутри возвращает 40, как и должно быть?

Ссылка: http://jsfiddle.net/u8y8zh72/3/

<html>
<head>
    <script src="https://code.jquery.com/jquery-2.1.4.js"></script>
    <style>
        footer {
            height: 75px;
            position: fixed;
            bottom: 0;
        }
    </style>
</head>
<body>
    <div class="container">
    <ul id="js-news" class="js-hidden"></ul>
    </div>
    <footer>
        <div class="container" id="newswiretxt">
        <span></span>
        </div>
    </footer>
</body>
<script type="text/javascript">
    var newsWire = [];
    function loadNewswire() {
        $.getJSON('http://api.nytimes.com/svc/news/v3/content/all/all.json',
        {'api-key': 'XXXXXXXXX'},
        function(data) {
            console.log(data)
            var newsWireTemp = [];
            for (var i = 0; i < data.results.length; i++) {
                var breakingNews = data.results[i];
                var breakingTitle = breakingNews.title;
                var breakingAbstract = breakingNews.abstract;
                newsWireTemp.push(breakingTitle);
                newsWireTemp.push(breakingAbstract);
            }
            newsWire = newsWireTemp;
            console.log(newsWire.length);
        });
    }
    loadNewswire();
    console.log(newsWire.length);


    $(document).ready(function() {
    var items = newsWire;
    $text = $('#newswiretxt span'),
    delay = 10; //seconds
    function loop (delay) {
        $.each(items, function (i, elm){
            $text.delay(delay*1E3).fadeOut();
            $text.queue(function(){
                $text.html(items[i]);
                $text.dequeue();
            });
            $text.fadeIn();
            $text.queue(function(){
                if (i == items.length -1) {
                    loop(delay);   
                }
            $text.dequeue();
            });
        });
    }
    loop(delay);
    });
</script>


person Jeppe Gelardi Madsen    schedule 09.11.2015    source источник
comment
loadNewswire() делает асинхронный вызов. Это означает, что console.log(newsWire.length); выполняется перед обратным вызовом (newsWire = newsWireTemp;). Вы можете сказать это, потому что вы console.log(newsWire.length) выводятся после console.log(newsWire.length);   -  person Juan Mendes    schedule 09.11.2015


Ответы (1)


Главное вот что:

...
loadNewswire();
console.log(newsWire.length);
...

Когда вы вызываете loadNewsWire, вы запускаете асинхронный запрос JSON. Однако выполнение скрипта не будет ждать завершения этой функции, поэтому он немедленно запустит следующий оператор console.log. В этот момент запрос JSON еще не завершен, поэтому массив newsWire все еще пуст, поэтому console.log(newsWire.length) возвращает там 0.

Внутри вашей функции loadNewsWire у вас есть функция обратного вызова, которая выполняется, когда запрос JSON возвращает ваши данные. В этот момент вы заполняете массив, и console.log(newsWire.length) дает вам ожидаемое количество.


Обновление в ответ на комментарий:

Есть ли способ заставить остальную часть моего кода ждать выполнения функции?

Да! $.getJSON — это удобная оболочка для $.ajax, которая возвращает объект jqXHR (подробности в документации jQuery< /а>). Вы можете добавить дополнительные обратные вызовы к этому объекту, что вы фактически делаете в своем вызове $.getJSON. Следующее:

$.getJSON('http://...', { 'api-key': '...' },
    function (data) {
        // stuff
    });

Эквивалентно:

$.getJSON('http://...', { 'api-key': '...' })
    .done(function (data) {
        // stuff
    });

Таким образом, если вы измените свой loadNewswire, чтобы он возвращал объект, возвращенный из $.getJSON, вы можете прикрепить к нему обратный вызов, который будет ждать завершения асинхронной операции, и поместить в него остальную часть вашего кода. Если вы измените свой код на это:

function loadNewswire() {
    return $.getJSON(
        ...
    );
};

Затем вы можете обернуть код, который хотите подождать, используя один из обратных вызовов done, fail или always.

Тогда ваш код вызова будет выглядеть так:

loadNewswire().done(function () {
    console.log(newsWire.length);
    // and any other code you want to hold until the async operation is complete
});

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

person Martin Wedvich    schedule 09.11.2015
comment
Спасибо, это была и моя мысль, так как я мог видеть время в своей консоли, когда появлялись числа. Есть ли способ заставить остальную часть моего кода ждать выполнения функции? - person Jeppe Gelardi Madsen; 09.11.2015
comment
@JeppeGelardiMadsen Обновленный ответ на ваш комментарий. - person Martin Wedvich; 09.11.2015