在继续之前等待多个异步调用完成

时间:2010-05-04 19:17:43

标签: javascript jquery asynchronous callback

所以,我有一个加载的页面,并通过jquery.get生成几个请求,用它们的值填充下拉列表。

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

    LoadContact();
};

然后调用LoadContact();这是另一个调用,当它返回时,它会填充表单上的所有字段。问题是通常情况下,下拉列表并非全部填充,因此无法将它们设置为正确的值。

我需要做的是,只有在其他方法完成并且回调完成执行后,LoadContact才会执行。

但是,我不想在下拉填充回调的末尾放置一堆标志,然后检查,并且在调用LoadContact()之前必须进行递归的setTimeout调用检查;

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列表(需要更好的唯一ID )。然后它从ajaxSend解除绑定,因此只跟踪我们关心的请求。它还绑定到ajaxComplete并在返回时从堆栈中删除项目。当堆栈达到零时,它会执行我们的回调,并取消绑定ajaxComplete事件。

4 个答案:

答案 0 :(得分:43)

您可以像这样使用.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()更为重要,但最好的做法是同时使用它们。

答案 1 :(得分:17)

展开Tom Lianza's answer$.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;
}

然后在所有三个ajax调用返回后调用LoadContact()并可选地执行它们各自的回调:

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

答案 2 :(得分:7)

如果您使用的是Jquery 1.5或更高版本,我怀疑Deferred对象是您最好的选择: http://api.jquery.com/category/deferred-object/

辅助方法的时候,也很不错: http://api.jquery.com/jQuery.when/

答案 3 :(得分:0)

但是,我不想在下拉填充回调的最后放置一堆标志,然后检查,并且在调用LoadContact之前必须进行递归的setTimeout调用检查();
不需要setTimeout。您只需检入每个回调函数,即填充所有三个列表(或者更好地设置计数器,在每个回调中增加它并等待它等于3)然后从回调中调用LoadContact。对我来说似乎很容易。

ajaxStop方法可能有用,我只是不太熟悉它。