Ожидание завершения нескольких асинхронных вызовов перед продолжением - PullRequest
55 голосов
/ 04 мая 2010

Итак, у меня есть страница, которая загружается и через 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.

Ответы [ 4 ]

43 голосов
/ 04 мая 2010

Вы можете использовать .ajaxStop() так:

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

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

17 голосов
/ 04 июля 2014

Расширение до Ответ Тома Лианзы , $.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);
7 голосов
/ 08 июня 2012

Если вы используете Jquery 1.5 или более позднюю версию, я подозреваю, что отложенный объект - ваша лучшая ставка: http://api.jquery.com/category/deferred-object/

Вспомогательный метод, когда, также довольно хорош: http://api.jquery.com/jQuery.when/

0 голосов
/ 04 мая 2010

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

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

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