JQuery, используя.when и толкая массив

Были в поиске обратных вызовов для ajax() ответы с использованием $.when Я все еще не уверен, как это работает полностью, но это то, что я хотел бы сделать ниже.

Когда пользователь добавляет город и страну в строке, он переходит на URL в .ajax() Я получаю ответ, и он толкает массив для использования за пределами .each() петля.

На данный момент вы увидите здесь, на jsbin, что когда button сначала нажимается ответ в console.log [] затем, когда я нажимаю его снова, адреса появляются. затем третье нажатие снова добавит адреса, чего не должно быть.

JQuery

var addresses,town;
var arrayLocation = [];

$('button').click(function(){
    addresses = function() {
        deferred = new $.Deferred();
        var arrayOfLines = $('#gps').val().split('\n');
        $.each(arrayOfLines, function(index, item) {
            town = item.split(',');
            $.ajax({
                url: 'http://maps.googleapis.com/maps/api/geocode/json?address='+town[0]+'&sensor=false',
                dataType: 'json',
                success: function (data) {
                    add = data.results[0].address_components[0].long_name;
                    lat = data.results[0].geometry.location.lat;
                    lng = data.results[0].geometry.location.lng;
                    arrayLocation.push("['"+add+"', "+lat+", "+lng+"]");
                    console.log("['"+add+"', "+lat+", "+lng+"]");
                }
            });
        });
        return arrayLocation;
    };
    $.when(addresses()).then(function(arrayLocation){
        console.log(arrayLocation);
    });
});

2 ответа

Решение

Вы не используете $.when правильно. Основная проблема заключается в том, что addresses Функция возвращает пустой массив. Это правда, что этот массив будет заполнен в будущем, когда асинхронная операция (набор вызовов AJAX) завершится, но с точки зрения addressesАбоненту это невозможно узнать. Поэтому у звонящего абсолютно нет шансов отреагировать на завершающуюся операцию.

Что вы обычно делаете, это вернуть возвращаемое значение $.ajax звонящему, примерно так:

addresses = function() {
    return $.ajax({ ... });
};

Звонивший мог тогда сделать

$.when(addresses()).then(function(result) { ... });

Однако в этом конкретном примере это невозможно сделать напрямую, поскольку выполняется несколько вызовов AJAX, поэтому вам нужен какой-то способ "объединить" их все в один пакет. Есть несколько способов сделать это, поэтому есть вопрос, что вы предпочитаете здесь.

Одним из решений будет использование массива обещаний AJAX:

$('button').click(function(){
    var arrayLocation = [];
    addresses = function() {
        var promises = [];
        var arrayOfLines = $('#gps').val().split('\n');
        $.each(arrayOfLines, function(index, item) {
            town = item.split(',');
            promises.push($.ajax({
                url: 'http://maps.googleapis.com/...',
                dataType: 'json',
                success: function (data) {
                    add = data.results[0].address_components[0].long_name;
                    lat = data.results[0].geometry.location.lat;
                    lng = data.results[0].geometry.location.lng;
                    arrayLocation.push("['"+add+"', "+lat+", "+lng+"]");
                    console.log("['"+add+"', "+lat+", "+lng+"]");
                }
            }));
        });
        return promises;
    };

    $.when.apply($, addresses()).then(function(){
        console.log(arrayLocation);
    });
});

Здесь есть пара замечаний:

  1. Возвращение массива независимых обещаний означает, что вы не можете передать их $.when непосредственно, потому что эта функция предназначена для принятия нескольких отдельных обещаний вместо массива; вам нужно использовать apply компенсировать.
  2. Я перенес декларацию arrayLocation внутри обработчика события click, чтобы он сбрасывался при каждом нажатии кнопки. Проблема результатов, добавляемых снова после каждого клика, была связана с тем, что этот массив не сбрасывался.
  3. Последний обработчик не принимает никаких аргументов. Это потому, что аргументы, которые будут переданы, являются объектами jqXHR, представляющими отдельные запросы AJAX, что не очень полезно. Вместо этого он захватывает arrayLocation закрытием, так как вы независимо знаете, что результаты будут храниться там.

Немного другой подход без общих глобальных переменных

$('button').click(function () {
    var addresses = function () {
        var arrayOfLines = $('#gps').val().split('\n'),
            arrayLocation = [];
        $.each(arrayOfLines, function (index, item) {
            var town = item.split(',');
            var xhr = $.ajax({
                url: 'http://maps.googleapis.com/maps/api/geocode/json?address=' + $.trim(town[0]) + '&sensor=false',
                dataType: 'json'
            });
            arrayLocation.push(xhr);
        });

        return $.when.apply($, arrayLocation).then(function () {
            return $.map(arguments, function (args) {
                if (!$.isArray(args[0].results) || args[0].results.length == 0) {
                    return undefined;
                }
                var data = args[0].results[0];
                var location = data.geometry.location;

                var add = data.address_components[0].long_name;
                var lat = location.lat;
                var lng = lng;

                return "['" + add + "', " + lat + ", " + lng + "]";
            });
        });

        return arrayLocation;
    };

    addresses().done(function (arrayLocation) {
        console.log(arrayLocation)
    })
});

Демо: скрипка

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