【问题标题】:Waiting on multiple asynchronous calls to complete before continuing在继续之前等待多个异步调用完成
【发布时间】:2011-02-15 15:11:42
【问题描述】:

所以,我有一个加载页面,并通过 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 个月前问的重复问题吗?

标签: javascript jquery asynchronous callback


【解决方案1】:

你可以像这样使用.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() 更重要,但最好同时使用两者。

【讨论】:

  • 这很好,并且似乎在我的场景中工作,但是,让我们添加一个警告,有第四个异步调用需要很长时间,我不想等待 LoadContact不需要它是完整的。反正有没有说只需要完成这三件事?
  • @Chad - 不,如果你想挑挑拣拣……你必须自己设置一个计数器,全局处理程序无法知道你关心哪些。
  • 我添加了一个似乎可以满足我要求的解决方案。您的回答肯定为我指明了正确的方向!
  • 给了你答案,因为在我添加警告之前它是正确的。
  • +1 将我介绍给ajaxStop。我现在有 recusive async 多个负载工作。干杯
【解决方案2】:

扩展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);

【讨论】:

    【解决方案3】:

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

    辅助方法when,也相当不错: http://api.jquery.com/jQuery.when/

    【讨论】:

    • 同意,这就是我现在使用的
    • deferred 的哪一部分解决了这个问题?有很多选择,很难说如何解决这个问题。
    【解决方案4】:

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

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

    【讨论】:

    • 这是我要避免的。回调应该什么都不知道,也不关心在它们之后运行什么,或者依赖它们,或者其他回调是否正在执行或需要执行。这只是引入了非常复杂的逻辑。
    • 我看到一个简单的解决方案:使用您的逻辑将辅助函数作为参数传递给 LoadCategories 等('executeMeAfterCallback')。很像 ajaxStop,但可以让您更好地控制要参与的请求。
    • 顺便说一句,您是否有理由使用三种不同的方法来加载列表而不是一种(带有必要的参数,如 url)?只是好奇。
    • 它们都使用正确的 url 和目标元素调用一个通用函数。三个函数封装了这个通用方法的原因是为了保持代码的干净,如果我以后因为某种原因需要重新加载部门怎么办?或者也将它们加载到另一个控件中,我想要隐藏它的实现方式,因此,只需调用 LoadDepartments(targetElement);
    • 是的,这很好。不过,executeMeAfterCallback 应该这样做。以后您可以随时调用 LoadDepartments 而无需第二个参数。
    猜你喜欢
    • 2018-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-14
    • 1970-01-01
    • 1970-01-01
    • 2019-02-10
    相关资源
    最近更新 更多