Обратный вызов jQuery для нескольких вызовов ajax - PullRequest
120 голосов
/ 06 декабря 2010

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

Ответы [ 14 ]

141 голосов
/ 20 ноября 2011

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

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

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

98 голосов
/ 06 декабря 2010

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

ВНИМАНИЕ

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

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

Пример отсрочки здесь

для jQuery <1.5 будет работать следующее, или если вам нужно, чтобы ваши вызовы ajax запускались в неизвестное время, как показано здесь, с двумя кнопками: <a href="http://jsfiddle.net/EN8nc/165/" rel="noreferrer"> запускается после нажатия обеих кнопок

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

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

// 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);
        };
    };
})();
10 голосов
/ 06 декабря 2010

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

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

    function handleBefore() {
        inProgress++;
    };

    function handleComplete() {
        if (!--inProgress) {
            // do what's 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
        }
    });
});
9 голосов
/ 02 июня 2016

Стоит отметить, что, поскольку $.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]);
4 голосов
/ 06 декабря 2010

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

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

[Отредактировано для отражения обновлений имени.]

3 голосов
/ 06 декабря 2010

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


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

var sync = {
   count: 0
}

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

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

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

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

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

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

0 голосов
/ 06 января 2019

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

JS

$.ajax({
  type: 'POST',
  url: 'ajax1.php',
  data:{
    id: 1,
    cb:'method1'//declaration of callback method of ajax1.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method1'
  eval(cb+"(JSON.stringify(data));");//here we calling method1 and pass all data
  }
});


$.ajax({
  type: 'POST',
  url: 'ajax2.php',
  data:{
    id: 2,
    cb:'method2'//declaration of callback method of ajax2.php
  },
  success: function(data){
  //catching up values
  var data = JSON.parse(data);
  var cb=data[0].cb;//here whe catching up the callback 'method2'
  eval(cb+"(JSON.stringify(data));");//here we calling method2 and pass all data
  }
});


//the callback methods
function method1(data){
//here we have our data from ajax1.php
alert("method1 called with data="+data);
//doing stuff we would only do in method1
//..
}

function method2(data){
//here we have our data from ajax2.php
alert("method2 called with data="+data);
//doing stuff we would only do in method2
//..
}

PHP (ajax1.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method1'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax1"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>

PHP (ajax2.php)

<?php
    //catch up callbackmethod
    $cb=$_POST['cb'];//is 'method2'

    $json[] = array(
    "cb" => $cb,
    "value" => "ajax2"
    );      

    //encoding array in JSON format
    echo json_encode($json);
?>
0 голосов
/ 20 октября 2018

Хорошо, это старо, но, пожалуйста, позвольте мне внести свое решение:)

function sync( callback ){
    syncCount--;
    if ( syncCount < 1 ) callback();
}
function allFinished(){ .............. }

window.syncCount = 2;

$.ajax({
    url: 'url',
    success: function(data) {
        sync( allFinished );
    }
});
someFunctionWithCallback( function(){ sync( allFinished ); } )

Работает также с функциями, имеющими обратный вызов. Вы устанавливаете syncCount и вызываете функцию sync (...) при обратном вызове каждого действия.

0 голосов
/ 07 января 2013
async   : false,

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

0 голосов
/ 08 сентября 2012

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...