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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2768293/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 01:51:15  来源:igfitidea点击:

Waiting on multiple asynchronous calls to complete before continuing

javascriptjqueryasynchronouscallback

提问by CaffGeek

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 InfoI 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 事件的绑定。

采纳答案by Nick Craver

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(),但最好同时使用两者。

回答by KPD

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:

唯一需要注意的是,您需要确保需要等待的异步方法返回 a Deferred object。幸运的是,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);

回答by Tom Lianza

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/

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

回答by Nikita Rybak

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 方法可能适用,我只是不太熟悉它。