Обратный вызов jQuery для нескольких вызовов ajax

119

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

$('#button').click(function() {
    fun1();
    fun2();
    fun3();
//now do something else when the requests have done their 'success' callbacks.
});

var fun1= (function() {
    $.ajax({/*code*/});
});
var fun2 = (function() {
    $.ajax({/*code*/});
});
var fun3 = (function() {
    $.ajax({/*code*/});
});
Теги:
callback

12 ответов

89
Лучший ответ

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

УВЕДОМЛЕНИЕ

Так как jQuery 1.5+ вы можете использовать отложенный метод, как описано в другом ответе:

  $.when($.ajax(), [...]).then(function(results){},[...]);

Пример отложенного здесь

для jQuery < 1.5 будет работать следующее, или если вам нужно, чтобы ваши вызовы ajax были запущены в неизвестные моменты, как показано здесь, с помощью двух кнопок: запускается после нажатия обеих кнопок

[использование]

для завершения одиночного обратного вызова: Рабочий пример

// initialize here
var requestCallback = new MyRequestsCompleted({
    numRequest: 3,
    singleCallback: function(){
        alert( "I'm the callback");
    }
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.requestComplete(true);
    }
});

каждый имеет свой собственный обратный вызов, когда все завершено: Рабочий пример

//initialize 
var requestCallback = new MyRequestsCompleted({
    numRequest: 3
});

//usage in request
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the first callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the second callback');
        });
    }
});
$.ajax({
    url: '/echo/html/',
    success: function(data) {
        requestCallback.addCallbackToQueue(true, function() {
            alert('Im the third callback');
        });
    }
});

[Код]

var MyRequestsCompleted = (function() {
    var numRequestToComplete, requestsCompleted, callBacks, singleCallBack;

    return function(options) {
        if (!options) options = {};

        numRequestToComplete = options.numRequest || 0;
        requestsCompleted = options.requestsCompleted || 0;
        callBacks = [];
        var fireCallbacks = function() {
            alert("we're all complete");
            for (var i = 0; i < callBacks.length; i++) callBacks[i]();
        };
        if (options.singleCallback) callBacks.push(options.singleCallback);

        this.addCallbackToQueue = function(isComplete, callback) {
            if (isComplete) requestsCompleted++;
            if (callback) callBacks.push(callback);
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.requestComplete = function(isComplete) {
            if (isComplete) requestsCompleted++;
            if (requestsCompleted == numRequestToComplete) fireCallbacks();
        };
        this.setCallback = function(callback) {
            callBacks.push(callBack);
        };
    };
})();
  • 1
    Спасибо за отличный пример кода! Я думаю, что смогу споткнуться через все остальное. Еще раз спасибо!
  • 0
    Нет проблем, рад, что это помогло! Я немного увлекся этим: P еще есть идеи для этого. Я планирую обновить его там, где вам не нужно указывать количество запросов на инициализацию.
Показать ещё 9 комментариев
142

Похоже, у вас есть ответы на это, но я думаю, что здесь стоит упомянуть, что значительно упростит ваш код. jQuery представил $.when в версии 1.5. Это выглядит так:

$.when($.ajax(...), $.ajax(...)).then(function (resp1, resp2) {
    //this callback will be fired once all ajax calls have finished.
});

Не видел, чтобы это упоминалось здесь, надеюсь, что это поможет.

  • 6
    Спасибо за ответ, это определенно способ для меня. он использует jQuery, он компактен, короток и выразителен.
  • 2
    Ответ Subhaze великолепен, но на самом деле это правильный ответ.
Показать ещё 1 комментарий
8

Не видя необходимости в каком-либо объекте malarky. У простого есть переменная, которая является целым числом. Когда вы начинаете запрос, увеличьте число. Когда он заканчивается, уменьшите его. Когда он равен нулю, запросы не выполняются, поэтому вы закончили.

$('#button').click(function() {
    var inProgress = 0;

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what in here when all requests have completed.
        }
    };

    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
    $.ajax({
        beforeSend: handleBefore,
        complete: function () {
            // whatever
            handleComplete();
            // whatever
        }
    });
});
  • 0
    Это очень просто и отлично работает ... Не могли бы вы объяснить немного лучше, что происходит в if (--inProgress)? Он должен контролировать, если переменная inProgress == 0 и вычитать одну единицу, но я не получаю компактный синтаксис ...
  • 1
    @Pitto: Упс ... на самом деле, там есть логическая ошибка, и это должно быть, if (!--inProgress) . Правильная версия аналогична inProgress = inProgress - 1; if (inProgress == 0) { /* do stuff */ } . Компактная форма объединяет префиксный оператор декремента ( -- ) и оператор отрицания ( ! ) Для оценки в значение «истина» (и, следовательно, для выполнения блока if ), если уменьшение значения inProgress приводит к inProgress == 0 , а значение «ложь» (и, следовательно, ничего не делает) в противном случае.
Показать ещё 3 комментария
7

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

// Save all requests in an array of jqXHR objects
var requests = arrayOfThings.map(function(thing) {
    return $.ajax({
        method: 'GET',
        url: 'thing/' + thing.id
    });
});
$.when.apply(this, requests).then(function(resp1, resp2/*, ... */) {
    // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
    var responseArgsArray = Array.prototype.slice.call(this, arguments);

});

Это потому, что $.when принимает такие аргументы как

$.when(ajaxRequest1, ajaxRequest2, ajaxRequest3);

И не так:

$.when([ajaxRequest1, ajaxRequest2, ajaxRequest3]);
  • 0
    Отличный ответ. Сейчас есть много хороших обещаний, у большинства из них есть метод all или что-то подобное, но мне нравится концепция карты. Ницца.
  • 1
    Да, я почти всегда использую $ .when, применяя к нему массив запросов, и не нашел этого в решении здесь, поэтому просто добавил его, чтобы иметь хороший пример здесь.
3

Мне нравится идея hvgotcodes. Мое предложение состоит в том, чтобы добавить общий инкремент, который сравнивает число, полное и необходимое число, а затем выполняет окончательный обратный вызов. Это может быть встроено в окончательный обратный вызов.

var sync = {
 callbacksToComplete = 3,
 callbacksCompleted = 0,
 addCallbackInstance = function(){
  this.callbacksCompleted++;
  if(callbacksCompleted == callbacksToComplete) {
   doFinalCallBack();
  }
 }
};

[Отредактировано, чтобы отображать обновления имен.]

  • 1
    отличное улучшение
  • 0
    ^ Согласились, помогли лучше понять, что мне на самом деле нужно.
3

EDIT - возможно, лучшим вариантом будет создание конечной точки службы, которая выполняет все три запроса. Таким образом, вам нужно сделать только один запрос, и все данные там, где вам нужно, чтобы они были в ответе. Если вы обнаружите, что повторяете один и тот же 3 запроса снова и снова, вы, вероятно, захотите пойти по этому маршруту. Часто правильное дизайнерское решение заключается в настройке службы фасада на сервере, которая объединяет общие действия меньшего сервера. Просто идея.


Один из способов сделать это - создать объект "sync" в обработчике кликов перед вызовами ajax. Что-то вроде

var sync = {
   count: 0
}

Синхронизация будет автоматически связана с объемом вызовов успеха (закрытие). В обработчике успеха вы увеличиваете счетчик, а если он равен 3, вы можете вызвать другую функцию.

В качестве альтернативы вы можете сделать что-то вроде

var sync = {
   success1Complete: false,
   ...
   success3Complete: false,
}

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

Обратите внимание на случай, когда один из ваших xhrs не возвращает успех - вам нужно учитывать это.

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

  • 0
    Мне больше нравится ваше первое предложение по нескольким причинам. Каждый запрос имеет свою цель, поэтому я бы хотел оставить их отдельно по этой причине. Кроме того, запросы на самом деле находятся в функциях, потому что я использую их в другом месте, поэтому я старался сохранить модульный дизайн, если это возможно. Спасибо за вашу помощь!
  • 0
    @Jisaak, да, если для решения этой проблемы на сервере не будет никакого смысла, это может не иметь смысла. Но допустимо настроить фасад на сервере. Вы говорите о модульности, создание одного метода, который обрабатывает все три вещи, является модульным.
0
async   : false,

По умолчанию все запросы отправляются асинхронно (т.е. по умолчанию это значение равно true). Если вам нужны синхронные запросы, установите для этого параметра значение false. Кросс-доменные запросы и запросы dataType: "jsonp" не поддерживают синхронную работу. Обратите внимание, что синхронные запросы могут временно блокировать браузер, отключая любые действия, пока запрос активен. Начиная с jQuery 1.8 использование async: false с jqXHR ($.Deferred) устарело; вы должны использовать параметры обратного вызова success/error/complete вместо соответствующих методов объекта jqXHR, например jqXHR.done() или устаревшего jqXHR.success().

  • 0
    Это не адекватное решение. Вы никогда не должны делать синхронный Ajax-запрос.
0

Это не jquery (и похоже, что jquery имеет работоспособное решение), но как еще один вариант....

У меня были схожие проблемы с работой с веб-службами SharePoint - вам часто нужно извлекать данные из нескольких источников для создания ввода для одного процесса.

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

DPAJAX на DepressedPress.com

Этот простой пример создает один запрос с тремя вызовами и затем передает эту информацию в порядке вызова одному обработчику:

    // The handler function 
function AddUp(Nums) { alert(Nums[1] + Nums[2] + Nums[3]) }; 

    // Create the pool 
myPool = DP_AJAX.createPool(); 

    // Create the request 
myRequest = DP_AJAX.createRequest(AddUp); 

    // Add the calls to the request 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [5,10]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [4,6]); 
myRequest.addCall("GET", "http://www.mysite.com/Add.htm", [7,13]); 

    // Add the request to the pool 
myPool.addRequest(myRequest); 

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

Я нашел его безумно полезным (и невероятно простым для понимания с точки зрения кода). Больше цепочки, больше нет счетных вызовов и экономии выхода. Просто "установите его и забудьте".

0

Сегодня я столкнулся с этой проблемой, и это была моя наивная попытка, прежде чем смотреть принятый ответ.

<script>
    function main() {
        var a, b, c
        var one = function() {
            if ( a != undefined  && b != undefined && c != undefined ) {
                alert("Ok")
            } else {
                alert( "¬¬ ")
            }
        }

        fakeAjaxCall( function() {
            a = "two"
            one()
        } )
        fakeAjaxCall( function() {
            b = "three"
            one()
        } )
        fakeAjaxCall( function() {
            c = "four"
            one()
        } )
    }
    function fakeAjaxCall( a ) {
        a()
    }
    main()
</script>
0

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

// lets say we have 2 ajax functions that needs to be "synchronized". 
// In other words, we want to know when both are completed.
function foo1(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo1');
            callback();               
        }
    });
}

function foo2(callback) {
    $.ajax({
        url: '/echo/html/',
        success: function(data) {
            alert('foo2');
            callback();
        }
    });
}

// here is my simplified solution
ajaxSynchronizer = function() {
    var funcs = [];
    var funcsCompleted = 0;
    var callback;

    this.add = function(f) {
        funcs.push(f);
    }

    this.synchronizer = function() {
        funcsCompleted++;
        if (funcsCompleted == funcs.length) {
            callback.call(this);
        }
    }

    this.callWhenFinished = function(cb) {
        callback = cb;
        for (var i = 0; i < funcs.length; i++) {
            funcs[i].call(this, this.synchronizer);
        }
    }
}

// this is the function that is called when both ajax calls are completed.
afterFunction = function() {
    alert('All done!');
}

// this is how you set it up
var synchronizer = new ajaxSynchronizer();
synchronizer.add(foo1);
synchronizer.add(foo2);
synchronizer.callWhenFinished(afterFunction);

Здесь есть некоторые ограничения, но для моего случая все в порядке. Я также обнаружил, что для более продвинутых материалов есть также плагин AOP (для jQuery), который может быть полезен: http://code.google.com/p/jquery-aop/

0

Я задал тот же вопрос некоторое время назад и получил пару хороших ответов здесь: Лучший способ добавить "обратный вызов" после серии асинхронных вызовов XHR

-1
$.ajax({type:'POST', url:'www.naver.com', dataType:'text', async:false,
    complete:function(xhr, textStatus){},
    error:function(xhr, textStatus){},
    success:function( data ){
        $.ajax({type:'POST', 
            ....
            ....
            success:function(data){
                $.ajax({type:'POST',
                    ....
                    ....
            }
        }
    });

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

  • 0
    Не следует использовать AJAX таким образом. Обратный звонок Ад!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню