Подтвердить что ты не робот

Как связать вызовы ajax с помощью jquery

Мне нужно сделать серию N запросов ajax без блокировки браузера, и вы хотите использовать объект отложенных jquery для выполнения этого.

Вот упрощенный пример с тремя запросами, но моей программе может потребоваться очередь более 100 (учтите, что это не точный прецедент, фактический код должен гарантировать успех этапа (N-1) до выполнение следующего шага):

$(document).ready(function(){

    var deferred = $.Deferred();

    var countries = ["US", "CA", "MX"];

    $.each(countries, function(index, country){

        deferred.pipe(getData(country));

    });

 });

function getData(country){

    var data = {
        "country": country  
    };


    console.log("Making request for [" + country + "]");

    return $.ajax({
        type: "POST",
        url: "ajax.jsp",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + country + "]");
        }
    });

}

Вот что записывается в консоль (все запросы выполняются параллельно, и время отклика прямо пропорционально размеру данных для каждой страны, как и ожидалось:

Making request for [US]
Making request for [CA]
Making request for [MX]
Successful request for [MX]
Successful request for [CA]
Successful request for [US]

Как я могу получить отложенный объект для очереди для меня? Я пробовал смену, сделанную для соединения, но получаю тот же результат.

Вот желаемый результат:

Making request for [US]
Successful request for [US]
Making request for [CA]
Successful request for [CA]
Making request for [MX]
Successful request for [MX]

Edit:

Я ценю предложение использовать массив для хранения параметров запроса, но объект отложенных jquery имеет возможность очереди запросов, и я действительно хочу узнать, как использовать эту функцию в полном объеме.

Это эффективно то, что я пытаюсь сделать:

when(request[0]).pipe(request[1]).pipe(request[2])... pipe(request[N]);

Тем не менее, я хочу назначить запросы в трубу на один шаг за раз, чтобы эффективно использовать каждый обход:

deferred.pipe(request[0]);
deferred.pipe(request[1]);
deferred.pipe(request[2]);
4b9b3361

Ответ 1

С пользовательским объектом

function DeferredAjax(opts) {
    this.options=opts;
    this.deferred=$.Deferred();
    this.country=opts.country;
}
DeferredAjax.prototype.invoke=function() {
    var self=this, data={country:self.country};
    console.log("Making request for [" + self.country + "]");

    return $.ajax({
        type: "GET",
        url: "wait.php",
        data: data,
        dataType: "JSON",
        success: function(){
            console.log("Successful request for [" + self.country + "]");
            self.deferred.resolve();
        }
    });
};
DeferredAjax.prototype.promise=function() {
    return this.deferred.promise();
};


var countries = ["US", "CA", "MX"], startingpoint = $.Deferred();
startingpoint.resolve();

$.each(countries, function(ix, country) {
    var da = new DeferredAjax({
        country: country
    });
    $.when(startingpoint ).then(function() {
        da.invoke();
    });
    startingpoint= da;
});

Fiddle http://jsfiddle.net/7kuX9/1/

Чтобы быть более понятным, последние строки могут быть записаны

c1=new DeferredAjax( {country:"US"} );
c2=new DeferredAjax( {country:"CA"} );
c3=new DeferredAjax( {country:"MX"} );

$.when( c1 ).then( function() {c2.invoke();} );
$.when( c2 ).then( function() {c3.invoke();} );

С трубами

function fireRequest(country) {
        return $.ajax({
            type: "GET",
            url: "wait.php",
            data: {country:country},
            dataType: "JSON",
            success: function(){
                console.log("Successful request for [" + country + "]");
            }
        });
}

var countries=["US","CA","MX"], startingpoint=$.Deferred();
startingpoint.resolve();

$.each(countries,function(ix,country) {
    startingpoint=startingpoint.pipe( function() {
        console.log("Making request for [" + country + "]");
        return fireRequest(country);
    });
});

http://jsfiddle.net/k8aUj/1/

Изменить: скрипка, выводимая журнал в окне результатов http://jsfiddle.net/k8aUj/3/

Каждый вызов канала возвращает новое обещание, которое, в свою очередь, используется для следующего канала. Обратите внимание, что я предоставлял только функцию sccess, для отказов должна быть предоставлена ​​аналогичная функция.

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

Я считаю, что пользовательский объект обеспечивает более простой способ манипулировать цепочкой, но трубы могут лучше соответствовать вашим вкусам.

Примечание: по состоянию на jQuery 1.8, deferred.pipe() устарела, deferred.then заменяет его.

Ответ 2

Примечание. С jquery 1.8 вы можете использовать .then вместо .pipe. Функция .then теперь возвращает новое обещание, а .pipe устарела, поскольку она больше не нужна. См. promises spec для получения дополнительной информации о promises, а q.js для более чистой библиотеки javascript promises без зависимости jquery.

countries.reduce(function(l, r){
  return l.then(function(){return getData(r)});
}, $.Deferred().resolve());

и если вы хотите использовать q.js:

//create a closure for each call
function getCountry(c){return function(){return getData(c)};}
//fire the closures one by one
//note: in Q, when(p1,f1) is the static version of p1.then(f1)
countries.map(getCountry).reduce(Q.when, Q());

Оригинальный ответ:

Еще одна труба; не для слабонервных, но немного более компактных:

countries.reduce(function(l, r){
  return l.pipe(function(){return getData(r)});
}, $.Deferred().resolve());

Уменьшить документацию, вероятно, лучшее место, чтобы начать понимать, как работает этот код. В принципе, он принимает два аргумента, обратный вызов и начальное значение.

Обратный вызов применяется итеративно по всем элементам массива, где его первый аргумент получает результат предыдущей итерации, а второй аргумент - текущий элемент. Трюк здесь заключается в том, что getData() возвращает jquery отложенное обещание, и канал гарантирует, что до того, как getData будет вызываться в текущем элементе, getData предыдущего элемента завершена.

Второй аргумент $.Deferred().resolve() - это идиома для разрешенного отложенного значения. Он подается на первую итерацию выполнения обратного вызова и гарантирует, что getData на первом элементе будет немедленно вызван.

Ответ 3

Я не совсем уверен, почему вы хотите это сделать, но сохраните список всех URL-адресов, которые вам нужно запросить, и не запрашивайте следующий, пока не будет вызвана функция success. I.E., success условно вызовет дополнительные вызовы deferred.

Ответ 4

Я знаю, что я опаздываю на это, но я считаю, что ваш исходный код в основном прекрасен, но имеет две (возможно, три) проблемы.

Ваш getData(country) вызывается немедленно из-за того, как вы закодировали свой параметр pipe. То, как вы это делаете, getData() выполняется немедленно, а результат (посыл ajax, но HTTP-запрос начинается сразу) передается как параметр pipe(). Таким образом, вместо передачи функции обратного вызова вы передаете объект, что приводит к немедленному разрешению нового канала.

Я думаю, что это должно быть

deferred.pipe(function () { return getData(country); });

Теперь это функция обратного вызова, которая будет вызываться, когда родительский отложенный канал был разрешен. Его кодирование таким образом поднимет вторую проблему. Ни один из параметров getData() не будет выполняться до тех пор, пока не будет разрешен отложенный мастер.

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

Итак, я думаю, вам нужно что-то вроде этого

var countries = ["US", "CA", "MX"];
var deferred = $.Deferred();
var promise = deferred.promise();

$.each(countries, function(index, country) {
    promise = promise.pipe(function () { return getData(country); });
});

deferred.resolve();

Ответ 5

Обновление: deferred.pipe устарела

Это много кода для того, что уже описано в API jQuery. см. http://api.jquery.com/deferred.pipe/

Вы можете просто поддерживать их до тех пор, пока не будет создано все 100.

Или я написал что-то, чтобы сделать N вызовов, и разрешить одну функцию с данными всех сделанных вызовов. Примечание: он возвращает данные, а не объект супер XHR. https://gist.github.com/1219564

Ответ 6

У меня был успех с очередями jQuery.

$(function(){
    $.each(countries, function(i,country){
      $('body').queue(function() {
        getData(country);
      });
    });
});

var getData = function(country){
  $.ajax({
    url : 'ajax.jsp',
    data : { country : country },
    type : 'post',
    success : function() {                          
      // Que up next ajax call
      $('body').dequeue();
    },
    error : function(){
      $('body').clearQueue();
    }
  });
};