关于javascript:等到所有jQuery Ajax请求都完成了?

Wait until all jQuery Ajax requests are done?

如何使一个函数等到另一个函数内完成所有jquery-ajax请求?

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


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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.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参数(例如,您希望使用可变数量的参数),那么仍然可以完成,但只是稍微有点棘手。请参见传入一个延迟到$.when()的数组(或者jquery.when用变量数目的参数进行故障排除)。

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


如果要等到文档中的所有Ajax请求都完成,不管它们有多少个,只需使用$.ajaxStop事件:

1
2
3
  $(document).ajaxStop(function () {
      // 0 === $.active
  });

在这种情况下,不需要猜测一个应用程序中将来可能会完成多少请求。在某些情况下,Ajax请求可能是函数内部逻辑的一部分,这可能非常复杂(例如调用其他函数),在这种情况下,您可能不会等到所述函数完成其整个逻辑,而只是等待ajax部分完成。

这里的$.ajaxStop也可以绑定到您认为可能被ajax修改的任何HTML节点。

同样,这个处理程序的目的是知道什么时候没有活动的ajax来清除或重置某些东西。

另外,如果您不介意使用ES6语法,那么您可以将Promise.all用于已知的ajax方法。例子:

1
2
3
4
5
Promise.all([ajax1(), ajax2()]).then(() => {
 // all requests finished successfully
}).catch(() => {
 // all requests finished but one or more failed
})

这里有趣的一点是,它既适用于Promises请求,也适用于$.ajax请求。下面是展示最后一个的jsiddle。


我找到了一个很好的答案,那正是我所寻找的。)

jquery半开队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//This handles the queues    
(function($) {

  var ajaxQueue = $({});

  $.ajaxQueue = function(ajaxOpts) {

    var oldComplete = ajaxOpts.complete;

    ajaxQueue.queue(function(next) {

      ajaxOpts.complete = function() {
        if (oldComplete) oldComplete.apply(this, arguments);

        next();
      };

      $.ajax(ajaxOpts);
    });
  };

})(jQuery);

然后您可以像这样向队列添加Ajax请求:

1
2
3
4
5
6
7
8
$.ajaxQueue({
        url: 'page.php',
        data: {id: 1},
        type: 'POST',
        success: function(data) {
            $('#status').html(data);
        }
    });


注意:以上答案使用的功能在编写此答案时不存在。我建议使用jQuery.when()而不是这些方法,但出于历史原因,我将保留这个答案。

-

您可能可以通过一个简单的计数信号量来实现它,尽管实现它的方式取决于您的代码。一个简单的例子是…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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队列完成相同的操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var $queue = $("");
$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();
    });
});


使用ajaxStop事件。

例如,假设您正在加载…获取100个Ajax请求时的消息,您希望在加载后隐藏该消息。

从jquery文档:

1
2
3
$("#loading").ajaxStop(function() {
  $(this).hide();
});

请注意,它将等待在该页上完成所有Ajax请求。


javascript是基于事件的,所以您不应该等待,而应该设置钩子/回调

您可能只需使用jquery.ajax的success/complete方法即可。

或者可以使用.ajaxComplete:

1
2
3
4
5
6
$('.log').ajaxComplete(function(e, xhr, settings) {
  if (settings.url == 'ajax/test.html') {
    $(this).text('Triggered ajaxComplete handler.');
    //and you can do whatever other processing here, including calling another function...
  }
});

尽管您应该发布一个伪代码,说明如何调用您的Ajax请求,以便更加精确…


一个小小的解决方法是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// Define how many Ajax calls must be done
var ajaxCalls = 3;
var counter = 0;
var ajaxCallComplete = function() {
    counter++;
    if( counter >= ajaxCalls ) {
            // When all ajax calls has been done
        // Do something like hide waiting images, or any else function call
        $('*').css('cursor', 'auto');
    }
};

var loadPersons = function() {
        // Show waiting image, or something else
    $('*').css('cursor', 'wait');

    var url = global.ctx + '/loadPersons';
    $.getJSON(url, function(data) {
            // Fun things
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCountries = function() {
    // Do things
    var url = global.ctx + '/loadCountries';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

var loadCities = function() {
    // Do things
    var url = global.ctx + '/loadCities';
    $.getJSON(url, function(data) {
            // Travels
    })
    .complete(function() { **ajaxCallComplete();** });
};

$(document).ready(function(){
    loadPersons();
    loadCountries();
    loadCities();
});

希望是有用的…


正如前面提到的其他答案,您可以使用ajaxStop()等待所有Ajax请求完成。

1
2
3
$(document).ajaxStop(function() {
     // This function will be triggered every time an ajax is requested and completed
});

但是,如果您希望针对特定的ajax()请求执行此操作,那么您可以在特定的ajax请求中使用complete()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
$.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.
    }
});

基于@bbonifield answer,我编写了一个实用函数,这样信号量逻辑就不会在所有Ajax调用中传播。

untilAjax是一个实用程序函数,当所有的ajaxCalls都完成时调用回调函数。

ajaxObjs是Ajax设置对象的数组[http://api.jquery.com/jQuery.ajax/]

fn是回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function untilAjax(ajaxObjs, fn) {
  if (!ajaxObjs || !fn) {
    return;
  }
  var ajaxCount = ajaxObjs.length,
    succ = null;

  for (var i = 0; i < ajaxObjs.length; i++) { //append logic to invoke callback function once all the ajax calls are completed, in success handler.
    succ = ajaxObjs[i]['success'];
    ajaxObjs[i]['success'] = function(data) { //modified success handler
      if (succ) {
        succ(data);
      }
      ajaxCount--;
      if (ajaxCount == 0) {
        fn(); //modify statement suitably if you want 'this' keyword to refer to another object
      }
    };
    $.ajax(ajaxObjs[i]); //make ajax call
    succ = null;
  };

示例:doSomething函数使用untilAjax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function doSomething() {
  // variable declarations
  untilAjax([{
    url: 'url2',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url1',
    dataType: 'json',
    success: function(data) {
      //do something with success data
    }
  }, {
    url: 'url2',
    dataType: 'json',
    success: function(response) {
      //do something with success data
    }
  }], function() {
    // logic after all the calls are completed.
  });
}


为了进一步阐述亚历克斯的答案,我举了一个例子,其中有可变的论点和承诺。我想通过Ajax加载图像,并在它们全部加载后将它们显示在页面上。

为此,我使用了以下方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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/


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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)
})
1
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js">


如果您需要简单的东西;一次性完成回调

1
2
3
4
5
6
7
8
9
10
        //multiple ajax calls above
        var callback = function () {
            if ($.active !== 0) {
                setTimeout(callback, '500');
                return;
            }
            //whatever you need to do here
            //...
        };
        callback();


也可以使用async.js。

我认为它比$.when更好,因为您可以合并各种不支持开箱即用承诺的异步调用,如超时、sqllite调用等,而不仅仅是Ajax请求。


如果从头开始,我强烈建议使用$.when()。

尽管这个问题的答案超过了百万,但我仍然没有找到对我的案件有用的任何东西。假设您必须处理现有的代码库,已经进行了一些Ajax调用,并且不想引入承诺的复杂性和/或重做整个事情。

我们可以很容易地利用jquery .data.on.trigger功能,这些功能一直是jquery的一部分。

科德森

我的解决方案的优点是:

  • 很明显回调到底依赖于什么

  • 函数triggerNowOrOnLoaded不关心数据是否已经加载,或者我们还在等待它

  • 将其插入现有代码非常容易

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
$(function() {

  // wait for posts to be loaded
  triggerNowOrOnLoaded("posts", function() {
    var $body = $("body");
    var posts = $body.data("posts");

    $body.append("Posts:" + posts.length +"");
  });


  // some ajax requests
  $.getJSON("https://jsonplaceholder.typicode.com/posts", function(data) {
    $("body").data("posts", data).trigger("posts");
  });

  // doesn't matter if the `triggerNowOrOnLoaded` is called after or before the actual requests
  $.getJSON("https://jsonplaceholder.typicode.com/users", function(data) {
    $("body").data("users", data).trigger("users");
  });


  // wait for both types
  triggerNowOrOnLoaded(["posts","users"], function() {
    var $body = $("body");
    var posts = $body.data("posts");
    var users = $body.data("users");

    $body.append("Posts:" + posts.length +" and Users:" + users.length +"");
  });

  // works even if everything has already loaded!
  setTimeout(function() {

    // triggers immediately since users have been already loaded
    triggerNowOrOnLoaded("users", function() {
      var $body = $("body");
      var users = $body.data("users");

      $body.append("Delayed Users:" + users.length +"");
    });

  }, 2000); // 2 seconds

});

// helper function
function triggerNowOrOnLoaded(types, callback) {
  types = $.isArray(types) ? types : [types];

  var $body = $("body");

  var waitForTypes = [];
  $.each(types, function(i, type) {

    if (typeof $body.data(type) === 'undefined') {
      waitForTypes.push(type);
    }
  });

  var isDataReady = waitForTypes.length === 0;
  if (isDataReady) {
    callback();
    return;
  }

  // wait for the last type and run this function again for the rest of the types
  var waitFor = waitForTypes.pop();
  $body.on(waitFor, function() {
    // remove event handler - we only want the stuff triggered once
    $body.off(waitFor);

    triggerNowOrOnLoaded(waitForTypes, callback);
  });
}
1
2
3
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">

<body>Hi!</body>


$.when不适合我,callback(x)代替return x工作如下:https://stackoverflow.com/a/13455253/10357604


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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_计数器来解决它:https://bitback.org/stxnext/jquery_计数器/


看看我的解决方案:

1.将此函数(和变量)插入到您的javascript文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var runFunctionQueue_callback;

function runFunctionQueue(f, index, callback) {

  var next_index = index + 1

  if (callback !== undefined) runFunctionQueue_callback = callback;

  if (f[next_index] !== undefined) {
    console.log(index + ' Next function avalaible -> ' + next_index);
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      complete: function() {
        runFunctionQueue(f, next_index);
      }
    });
  } else {
    console.log(index + ' Last function');
    $.ajax({
      type: 'GET',
      url: f[index].file,
      data: (f[index].data),
      async: false,
      complete: runFunctionQueue_callback
    });
  }
}

2.用您的请求构建一个数组,如下所示:

1
2
3
4
5
6
var f = [
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: '
file_path', data: {action: 'action', data: 'any_data}},
           {file: 'file_path', data: {action: 'action', data: 'any_data}},
           {file: '
file_path', data: {action: 'action', data: 'any_data}}
        ];

3.创建回调函数:

1
2
3
function Function_callback() {
  alert('done');
}

4.用参数调用runfunctionqueue函数:

1
2
3
4
runFunctionQueue(f, 0, QuestionInsert_callback);
// first parameter: array with requests data
// second parameter: start from first request
// third parameter: the callback function

jquery允许您指定是否希望Ajax请求是异步的。您可以简单地使Ajax请求同步,然后剩下的代码在返回之前不会执行。

例如:

1
2
3
4
jQuery.ajax({
    async: false,
    //code
});


我的解决方案如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var request;
...
'services': {
  'GetAddressBookData': function() {
    //This is the primary service that loads all addressbook records
    request = $.ajax({
      type:"POST",
      url:"Default.aspx/GetAddressBook",
      contentType:"application/json;",
      dataType:"json"
    });
  },

  ...

  'apps': {
    'AddressBook': {
      'data':"",
      'Start': function() {
          ...services.GetAddressBookData();
          request.done(function(response) {
            trace("ajax successful");
            ..apps.AddressBook.data = response['d'];
            ...apps.AddressBook.Filter();
          });
          request.fail(function(xhr, textStatus, errorThrown) {
            trace("ajax failed -" + errorThrown);
          });

工作很好。我尝试过很多不同的方法,但我发现这是最简单和最可重用的方法。希望它有帮助


亚历克斯给出的解决方案很好。相同的概念,但使用方式略有不同(当提前不知道呼叫数时)

http://garbageoverflow.blogspot.com/2014/02/wait-for-n-or-multiple-or-unknown.html


试试这边。在Java脚本函数中生成一个循环,直到Ajax调用完成为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function getLabelById(id)
{
    var label = '';
    var done = false;
    $.ajax({
       cache: false,
       url:"YourMvcActionUrl",
       type:"GET",
       dataType:"json",
       async: false,
       error: function (result) {
         label='undefined';
         done = true;
        },
       success: function (result) {
            label = result.Message;
            done = true;
        }
     });

   //A loop to check done if ajax call is done.
   while (!done)
   {
      setTimeout(function(){ },500); // take a sleep.
   }

    return label;
}