我如何让一个函数等待,直到所有的jQuery Ajax请求在另一个函数内完成?

简而言之,在执行下一个Ajax请求之前,我需要等待所有Ajax请求都完成。但如何?


当前回答

为了扩展Alex的回答,我举了一个带有可变论点和承诺的例子。我想通过ajax加载图像,并在它们全部加载后显示在页面上。

为了做到这一点,我使用了以下方法:

let urlCreator = window.URL || window.webkitURL;

// Helper function for making ajax requests
let fetch = function(url) {
    return $.ajax({
        type: "get",
        xhrFields: {
            responseType: "blob"
        },
        url: url,
    });
};

// Map the array of urls to an array of ajax requests
let urls = ["https://placekitten.com/200/250", "https://placekitten.com/300/250"];
let files = urls.map(url => fetch(url));

// Use the spread operator to wait for all requests
$.when(...files).then(function() {
    // If we have multiple urls, then loop through
    if(urls.length > 1) {
        // Create image urls and tags for each result
        Array.from(arguments).forEach(data => {
            let imageUrl = urlCreator.createObjectURL(data[0]);
            let img = `<img src=${imageUrl}>`;
            $("#image_container").append(img);
        });
    }
    else {
        // Create image source and tag for result
        let imageUrl = urlCreator.createObjectURL(arguments[0]);
        let img = `<img src=${imageUrl}>`;
        $("#image_container").append(img);
    }
});

更新工作的单个或多个url: https://jsfiddle.net/euypj5w9/

其他回答

jQuery现在为此定义了一个when函数。

它接受任意数量的Deferred对象作为参数,并在所有这些对象都解析时执行函数。

这意味着,如果你想发起(例如)四个ajax请求,然后在它们完成时执行一个动作,你可以这样做:

$.when(ajax1(), ajax2(), ajax3(), ajax4()).done(function(a1, a2, a3, a4){
    // the code here will be executed when all four ajax requests resolve.
    // a1, a2, a3 and a4 are lists of length 3 containing the response text,
    // status, and jqXHR object for each of the four ajax calls respectively.
});

function ajax1() {
    // NOTE:  This function must return the value 
    //        from calling the $.ajax() method.
    return $.ajax({
        url: "someUrl",
        dataType: "json",
        data:  yourJsonData,            
        ...
    });
}

在我看来,这使得语法干净明了,并且避免涉及任何全局变量,如ajaxStart和ajaxStop,这可能会在页面开发过程中产生不必要的副作用。

如果你事先不知道你需要等待多少个ajax参数(即你想使用可变数量的参数),它仍然可以完成,只是有点棘手。参见在一个数组中传递deferred to $.when()(也可以是jQuery .当使用可变参数进行故障排除时)。

如果你需要更深入地控制ajax脚本的失败模式等,你可以保存.when()返回的对象-它是一个包含所有原始ajax查询的jQuery承诺对象。您可以调用.then()或.fail()来添加详细的成功/失败处理程序。

当所有ajax加载完成时,我使用大小检查

function get_ajax(link, data, callback) { $.ajax({ url: link, type: "GET", data: data, dataType: "json", success: function (data, status, jqXHR) { callback(jqXHR.status, data) }, error: function (jqXHR, status, err) { callback(jqXHR.status, jqXHR); }, complete: function (jqXHR, status) { } }) } function run_list_ajax(callback){ var size=0; var max= 10; for (let index = 0; index < max; index++) { var link = 'http://api.jquery.com/ajaxStop/'; var data={i:index} get_ajax(link,data,function(status, data){ console.log(index) if(size>max-2){ callback('done') } size++ }) } } run_list_ajax(function(info){ console.log(info) }) <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>

正如其他答案所提到的,你可以使用ajaxStop()等待直到所有ajax请求完成。

$(document).ajaxStop(function() {
     // This function will be triggered every time any ajax request is requested and completed
});

如果你想为特定的ajax()请求做这件事,你能做的最好的是在特定的ajax请求中使用complete()方法:

$.ajax({
    type: "POST",
    url: "someUrl",
    success: function(data) {
        // This function will be triggered when ajax returns a 200 status code (success)
    },
    complete: function() {
        // This function will be triggered always, when ajax request is completed, even it fails/returns other status code
    },
    error: function() {
        // This will be triggered when ajax request fail.
    }
});

但是,如果你只需要等待几个和特定的ajax请求被完成?使用美妙的javascript承诺等待,直到这些ajax你想等待完成。我做了一个简单易读的示例,向您展示promises如何与ajax一起工作。请看下一个例子。我使用setTimeout来阐明这个示例。

// Note: // resolve() is used to mark the promise as resolved // reject() is used to mark the promise as rejected $(document).ready(function() { $("button").on("click", function() { var ajax1 = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "https://miro.medium.com/max/1200/0*UEtwA2ask7vQYW06.png", xhrFields: { responseType: 'blob'}, success: function(data) { setTimeout(function() { $('#image1').attr("src", window.URL.createObjectURL(data)); resolve(" Promise ajax1 resolved"); }, 1000); }, error: function() { reject(" Promise ajax1 rejected"); }, }); }); var ajax2 = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "https://cdn1.iconfinder.com/data/icons/social-media-vol-1-1/24/_github-512.png", xhrFields: { responseType: 'blob' }, success: function(data) { setTimeout(function() { $('#image2').attr("src", window.URL.createObjectURL(data)); resolve(" Promise ajax2 resolved"); }, 1500); }, error: function() { reject(" Promise ajax2 rejected"); }, }); }); var ajax3 = new Promise((resolve, reject) => { $.ajax({ type: "GET", url: "https://miro.medium.com/max/632/1*LUfpOf7teWvPdIPTBmYciA.png", xhrFields: { responseType: 'blob' }, success: function(data) { setTimeout(function() { $('#image3').attr("src", window.URL.createObjectURL(data)); resolve(" Promise ajax3 resolved"); }, 2000); }, error: function() { reject(" Promise ajax3 rejected"); }, }); }); Promise.all([ajax1, ajax2, ajax3]).then(values => { console.log("We waited until ajax ended: " + values); console.log("My few ajax ended, lets do some things!!") }, reason => { console.log("Promises failed: " + reason); }); // Or if you want wait for them individually do it like this // ajax1.then(values => { // console.log("Promise 1 resolved: " + values) // }, reason => { // console.log("Promise 1 failed: " + reason) // }); }); }); img { max-width: 200px; max-height: 100px; } <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <button>Make AJAX request</button> <div id="newContent"> <img id="image1" src=""> <img id="image2" src=""> <img id="image3" src=""> </div>

我找到了一个简单的方法,使用shift()

function waitReq(id)
{
  jQuery.ajax(
  {
    type: 'POST',
    url: ajaxurl,
    data:
    {
      "page": id
    },
    success: function(resp)
    {
      ...........
      // check array length if not "0" continue to use next array value
      if(ids.length)
      {
        waitReq(ids.shift()); // 2
      )
    },
    error: function(resp)
    {
      ....................
      if(ids.length)
      {
        waitReq(ids.shift());
      )
    }
  });
}

var ids = [1, 2, 3, 4, 5];    
// shift() = delete first array value (then print)
waitReq(ids.shift()); // print 1

注意:上面的答案使用的功能在编写这个答案时并不存在。我建议使用jQuery.when()而不是这些方法,但我把答案留给历史用途。

-

您可能可以使用一个简单的计数信号量,尽管如何实现它取决于您的代码。一个简单的例子是……

var semaphore  = 0,     // counting semaphore for ajax requests
    all_queued = false; // bool indicator to account for instances where the first request might finish before the second even starts

semaphore++;
$.get('ajax/test1.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test2.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test3.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

semaphore++;
$.get('ajax/test4.html', function(data) {
    semaphore--;
    if (all_queued && semaphore === 0) {
        // process your custom stuff here
    }
});

// now that all ajax requests are queued up, switch the bool to indicate it
all_queued = true;

如果你想让它像{async: false}一样操作,但又不想锁定浏览器,你可以用jQuery队列来完成同样的事情。

var $queue = $("<div/>");
$queue.queue(function(){
    $.get('ajax/test1.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test2.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test3.html', function(data) {
        $queue.dequeue();
    });
}).queue(function(){
    $.get('ajax/test4.html', function(data) {
        $queue.dequeue();
    });
});