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

Я хочу сделать три вызова 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*/});
});

Ответ 1

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

УВЕДОМЛЕНИЕ

Так как 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);
        };
    };
})();

Ответ 2

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

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

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

Ответ 3

Не видя необходимости в каком-либо объекте 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
        }
    });
});

Ответ 4

Стоит отметить, что поскольку $.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]);

Ответ 5

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


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

var sync = {
   count: 0
}

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

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

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

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

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

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

Ответ 6

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

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

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

Ответ 8

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

// 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/

Ответ 9

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

<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>

Ответ 10

Это не 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) при условии, что этот метод не заставляет выполнять однопоточную обработку вызовов, каждый из которых будет выполняться так быстро (или медленно), как среда позволяет, но единственный обработчик будет вызываться только тогда, когда все будет завершено. Он также поддерживает настройку значений тайм-аута и попыток повторной попытки, если ваша служба немного ошибочна.

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

Ответ 11

async   : false,

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

Ответ 12

$.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 Потому что я корейский, который не может говорить ни слова на английском языке. но я думаю, вы можете легко это понять.