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

时间:2021-08-07 20:53:47

So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.

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

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

    LoadContact();
};

It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.

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

What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.

我需要做的是,只要其他方法完成并且回调完成执行,LoadContact就会以某种方式执行。

But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();

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

Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?

jQuery中有什么东西可以让我说,“当所有这些都完成时执行它。”?

More Info I am thinking something along these lines

更多信息我正在思考这些问题

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

...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;

...它需要跟踪在执行方法期间发生的ajax调用,并且当它们全部完成时,调用LoadContact;

If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.

如果我知道如何拦截在该函数中创建的ajax,我可能会编写一个jQuery扩展来执行此操作。

My Solution

我的解决方案

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

This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.

这会在调用方法时绑定到ajaxSend事件,并保留一个调用的url列表(需要更好的唯一id)。然后它从ajaxSend解除绑定,因此只跟踪我们关心的请求。它还绑定到ajaxComplete并在返回时从堆栈中删除项目。当堆栈达到零时,它将执行我们的回调,并取消绑定ajaxComplete事件。

4 个解决方案

#1


43  

You can use .ajaxStop() like this:

您可以像这样使用.ajaxStop():

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

This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.

这将在所有当前请求完成后运行,然后取消绑定,以便在页面执行中未来的ajax调用时它不会运行。另外,确保将它放在你的ajax调用之前,所以它得到了足够早的约束,对于.ajaxStart()来说更重要,但是最好的做法是同时使用它们。

#2


17  

Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().

扩展Tom Lianza的答案,$ .when()现在是比使用.ajaxStop()更好的方法。

The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:

唯一需要注意的是,您需要确保在返回Deferred对象时需要等待的异步方法。幸运的是,jQuery ajax调用默认情况下已经这样做了。因此,要从问题中实现场景,需要等待的方法看起来像这样:

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

Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:

然后在所有三个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


7  

If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet: http://api.jquery.com/category/deferred-object/

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

The helper method, when, is also quite nice: http://api.jquery.com/jQuery.when/

帮助方法,当时,也很不错:http://api.jquery.com/jQuery.when/

#4


0  

But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.

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

ajaxStop approach might work to, I'm just not very familiar with it.

ajaxStop方法可能有用,我对它不是很熟悉。

#1


43  

You can use .ajaxStop() like this:

您可以像这样使用.ajaxStop():

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

This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.

这将在所有当前请求完成后运行,然后取消绑定,以便在页面执行中未来的ajax调用时它不会运行。另外,确保将它放在你的ajax调用之前,所以它得到了足够早的约束,对于.ajaxStart()来说更重要,但是最好的做法是同时使用它们。

#2


17  

Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().

扩展Tom Lianza的答案,$ .when()现在是比使用.ajaxStop()更好的方法。

The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:

唯一需要注意的是,您需要确保在返回Deferred对象时需要等待的异步方法。幸运的是,jQuery ajax调用默认情况下已经这样做了。因此,要从问题中实现场景,需要等待的方法看起来像这样:

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

Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:

然后在所有三个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


7  

If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet: http://api.jquery.com/category/deferred-object/

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

The helper method, when, is also quite nice: http://api.jquery.com/jQuery.when/

帮助方法,当时,也很不错:http://api.jquery.com/jQuery.when/

#4


0  

But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.

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

ajaxStop approach might work to, I'm just not very familiar with it.

ajaxStop方法可能有用,我对它不是很熟悉。