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

Ожидание многократных асинхронных вызовов до завершения

Итак, у меня есть страница, которая загружается, и через jquery.get выполняется несколько запросов для заполнения выпадающих списков с их значениями.

$(function() {
    LoadCategories($('#Category'));
    LoadPositions($('#Position'));
    LoadDepartments($('#Department'));

    LoadContact();
};

Затем он вызывает LoadContact(); Что делает другой вызов, и когда он возвращает, он заполняет все поля формы. Проблема в том, что часто выпадающие списки не все заполняются, и, следовательно, они не могут установить правильное значение.

Что мне нужно сделать, так или иначе LoadContact выполняется только после завершения всех остальных методов и выполнения обратных вызовов.

Но я не хочу добавлять кучу флагов в конце обратных вызовов сбрасываемых демонов, которые затем проверяю и должен иметь рекурсивную проверку вызовов SetTimeout до вызова LoadContact();

Есть ли что-то в jQuery, которое позволяет мне сказать: "Выполните это, когда все это сделано".

Дополнительная информация Я думаю что-то в этом духе

$().executeAfter(
    function () {   // When these are done
        LoadCategories($('#Category'));
        LoadPositions($('#Position'));
        LoadDepartments($('#Department'));
    },
    LoadContact // Do this
);

... ему нужно будет отслеживать вызовы ajax, которые происходят во время выполнения методов, и когда все они будут завершены, вызовите LoadContact;

Если бы я знал, как перехватить ajax, который выполняется в этой функции, я мог бы, вероятно, написать расширение jQuery для этого.

Мое решение

;(function($) {
    $.fn.executeAfter = function(methods, callback) {

        var stack = [];

        var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
            var url = ajaxOptions.url;

            stack.push(url);
        }

        var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
            var url = ajaxOptions.url;

            var index = jQuery.inArray(url, stack);

            if (index >= 0) {
                stack.splice(index, 1);
            }

            if (stack.length == 0) {
                callback();
                $this.unbind("ajaxComplete");
            }
        }

        var $this = $(this);

        $this.ajaxSend(trackAjaxSend)
        $this.ajaxComplete(trackAjaxComplete)

        methods();
        $this.unbind("ajaxSend");
    };
})(jQuery);

Это связывается с событием ajaxSend при вызове методов и сохраняет список URL-адресов (нужен лучший уникальный идентификатор, хотя). Затем он отвязывает от ajaxSend, поэтому отслеживаются только те запросы, которые нас волнуют. Он также связывается с ajaxComplete и удаляет элементы из стека по мере их возвращения. Когда стек достигает нуля, он выполняет наш обратный вызов и отвязывает событие ajaxComplete.

4b9b3361

Ответ 1

Вы можете использовать .ajaxStop() следующим образом:

$(function() {
  $(document).ajaxStop(function() {
    $(this).unbind("ajaxStop"); //prevent running again when other calls finish
    LoadContact();
  });
  LoadCategories($('#Category'));
  LoadPositions($('#Position'));
  LoadDepartments($('#Department'));
});

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

Ответ 2

Расширение на Ответ Тома Лианзы, $.when() теперь очень лучший способ сделать это, чем использовать .ajaxStop().

Единственное предостережение в том, что вам нужно быть уверенным в асинхронных методах, которые вам нужно ждать по возвращении Deferred object. К счастью, jQuery вызовы ajax уже делают это по умолчанию. Поэтому для реализации сценария из вопроса методы, которые нужно ожидать, выглядят примерно так:

function LoadCategories(argument){
    var deferred = $.ajax({
       // ajax setup
    }).then(function(response){ 
       // optional callback to handle this response 
    });
    return deferred;
}

Затем, чтобы вызвать LoadContact() после того, как все три вызова ajax вернулись и, возможно, выполнили свои собственные индивидуальные обратные вызовы:

// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));

$.when(deferred1, deferred2, deferred3).then(LoadContact);

Ответ 4

Но я не хочу добавлять кучу флагов в конце обратных вызовов сбрасываемых демонов, которые затем проверяю и должен иметь рекурсивную проверку вызовов SetTimeout до вызова LoadContact();
Нет необходимости в setTimeout. Вы просто проверяете каждый обратный вызов, что все три списка заполнены (или лучше настроить счетчик, увеличить его в каждом обратном вызове и дождаться его равным 3), а затем вызвать LoadContact из обратного вызова. Мне кажется очень легким.

ajaxStop подход может работать, я просто не очень хорошо знаком с ним.