使用jQuery的并行异步Ajax请求

Pau*_*aul 73 javascript ajax jquery

我想根据多个ajax/json请求的结果更新页面.使用jQuery,我可以"链接"回调,就像这个非常简单的剥离示例:

$.getJSON("/values/1", function(data) {
  // data = {value: 1}
  var value_1 = data.value;

  $.getJSON("/values/2", function(data) {
    // data = {value: 42}
    var value_2 = data.value;

    var sum = value_1 + value_2;

    $('#mynode').html(sum);
  });

});
Run Code Online (Sandbox Code Playgroud)

但是,这导致请求是连续的.我更倾向于一种并行发出请求的方法,并在完成后执行页面更新.有没有办法做到这一点?

小智 113

jQuery $ .when()$ .done()正是您所需要的:

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);
Run Code Online (Sandbox Code Playgroud)

  • 关于如何在ajax调用完成后访问数据,这个答案并不清楚.传递给myFunc的是什么以及如何访问这些调用? (4认同)
  • 嗯,这应该是公认的答案。 (2认同)
  • 示例与 myFunc 和 myFailure => https://codepen.io/jacobgoh101/pen/YaJOzx?editors=0010 (2认同)

Yeh*_*atz 102

试试这个解决方案,它可以支持任何特定数量的并行查询:

var done = 4; // number of total requests
var sum = 0;

/* Normal loops don't create a new scope */
$([1,2,3,4,5]).each(function() {
  var number = this;
  $.getJSON("/values/" + number, function(data) {
    sum += data.value;
    done -= 1;
    if(done == 0) $("#mynode").html(sum);
  });
});
Run Code Online (Sandbox Code Playgroud)

  • 如果我没弄错的话,你是'jQuery In Action'的作者? (31认同)
  • 是的!我们正在谈论第二版:) (9认同)
  • 我选择了类似于你的东西和agilefall的:var results = {}; var requests = 0; var urls = ["values/1","values/2","values/3"]; $ .each(urls,function(url){$ .getJSON(url,function(data){results [url] = data.value; ++ requests; if(requests == 3){$('#mynode') .html(results [urls [0]]/results [urls [1]]*results [urls [2]]);}});}); (2认同)
  • 哦,不能格式化评论.这两个,格式化:http://gist.github.com/137804 (2认同)

Pet*_*ley 9

这是我试图直接解决你的问题

基本上,您只需构建和调用AJAX调用堆栈,执行所有事件,并在完成所有事件时调用提供的函数 - 提供的参数是来自所有提供的ajax请求的结果数组.

显然这是早期的代码 - 你可以在灵活性方面更加精细.

<script type="text/javascript" src="http://jqueryjs.googlecode.com/files/jquery-1.3.2.min.js"></script>
<script type="text/javascript">

var ParallelAjaxExecuter = function( onComplete )
{
  this.requests = [];
  this.results = [];
  this.onComplete = onComplete; 
}

ParallelAjaxExecuter.prototype.addRequest = function( method, url, data, format )
{
  this.requests.push( {
      "method"    : method
    , "url"       : url
    , "data"      : data
    , "format"    : format
    , "completed" : false
  } )
}

ParallelAjaxExecuter.prototype.dispatchAll = function()
{
  var self = this;
  $.each( self.requests, function( i, request )
    {
    request.method( request.url, request.data, function( r )
    {
      return function( data )
      {
        console.log
        r.completed = true;
        self.results.push( data );
        self.checkAndComplete();
      }
    }( request ) )
  } )
}

ParallelAjaxExecuter.prototype.allRequestsCompleted = function()
{
  var i = 0;
  while ( request = this.requests[i++] )
  {
    if ( request.completed === false )
    {
      return false;
    }
  }
  return true;
},

ParallelAjaxExecuter.prototype.checkAndComplete = function()
{
  if ( this.allRequestsCompleted() )
  {
    this.onComplete( this.results );
  }
}

var pe = new ParallelAjaxExecuter( function( results )
{
  alert( eval( results.join( '+' ) ) );
} );

pe.addRequest( $.get, 'test.php', {n:1}, 'text' );
pe.addRequest( $.get, 'test.php', {n:2}, 'text' );
pe.addRequest( $.get, 'test.php', {n:3}, 'text' );
pe.addRequest( $.get, 'test.php', {n:4}, 'text' );

pe.dispatchAll();

</script>
Run Code Online (Sandbox Code Playgroud)

这是test.php

<?php

echo pow( $_GET['n'], 2 );

?>
Run Code Online (Sandbox Code Playgroud)

  • OO走得太远了. (15认同)

pet*_*tys 9

更新:根据Yair Leviel给出的答案,这个答案已经过时了.使用promise库,如jQuery.when()或Q.js.


我创建了一个通用解决方案作为jQuery扩展.可以使用一些微调使其更通用,但它符合我的需要.截至撰写本文时,此技术优于其他技术的优势在于可以使用任何类型的带回调的异步处理.

注意:我会使用JavaScript的Rx扩展而不是这个,如果我认为我的客户端可以依赖于另一个第三方库:)

// jQuery extension for running multiple async methods in parallel
// and getting a callback with all results when all of them have completed.
//
// Each worker is a function that takes a callback as its only argument, and
// fires up an async process that calls this callback with its result.
//
// Example:
//      $.parallel(
//          function (callback) { $.get("form.htm", {}, callback, "html"); },
//          function (callback) { $.post("data.aspx", {}, callback, "json"); },
//          function (formHtml, dataJson) { 
//              // Handle success; each argument to this function is 
//              // the result of correlating ajax call above.
//          }
//      );

(function ($) {

    $.parallel = function (anyNumberOfWorkers, allDoneCallback) {

    var workers = [];
    var workersCompleteCallback = null;

    // To support any number of workers, use "arguments" variable to
    // access function arguments rather than the names above.
    var lastArgIndex = arguments.length - 1;
    $.each(arguments, function (index) {
        if (index == lastArgIndex) {
            workersCompleteCallback = this;
        } else {
            workers.push({ fn: this, done: false, result: null });
        }
    });

    // Short circuit this edge case
    if (workers.length == 0) {
        workersCompleteCallback();
        return;
    }

    // Fire off each worker process, asking it to report back to onWorkerDone.
    $.each(workers, function (workerIndex) {
        var worker = this;
        var callback = function () { onWorkerDone(worker, arguments); };
        worker.fn(callback);
    });

    // Store results and update status as each item completes.
    // The [0] on workerResultS below assumes the client only needs the first parameter
    // passed into the return callback. This simplifies the handling in allDoneCallback,
    // but may need to be removed if you need access to all parameters of the result.
    // For example, $.post calls back with success(data, textStatus, XMLHttpRequest).  If
    // you need textStatus or XMLHttpRequest then pull off the [0] below.
    function onWorkerDone(worker, workerResult) {
        worker.done = true;
        worker.result = workerResult[0]; // this is the [0] ref'd above.
        var allResults = [];
        for (var i = 0; i < workers.length; i++) {
            if (!workers[i].done) return;
            else allResults.push(workers[i].result);
        }
        workersCompleteCallback.apply(this, allResults);
    }
};

})(jQuery);
Run Code Online (Sandbox Code Playgroud)

  • 我已经使用了它,它工作得很好!...但我确实有一个改进:如果你修改最后的回调调用以使用"apply",那么你得到的回调单独的参数而不是一个参数列表:即workersCompleteCallback.apply(此,allResults); (2认同)

Dan*_*ker 7

更新另外两年后,这看起来很疯狂,因为接受的答案已经变得更好了!(虽然仍然不如Yair Leviel使用jQuery的答案when)

18个月后,我刚刚碰到了类似的东西.我有一个刷新按钮,我想要旧的内容fadeOut,然后新的内容fadeIn.但我还需要get新的内容.这是fadeOutget异步的,但串行运行它们会浪费时间.

除了可重用函数的形式外,我所做的与接受的答案完全相同.它的主要优点是它比这里的其他建议短得多.

var parallel = function(actions, finished) {

  finishedCount = 0;
  var results = [];

  $.each(actions, function(i, action) {

    action(function(result) {

      results[i] = result;
      finishedCount++;

      if (finishedCount == actions.length) {
        finished(results);
      }
    });
  });
};
Run Code Online (Sandbox Code Playgroud)

您传递一组函数以并行运行.每个函数都应该接受它传递结果的另一个函数(如果有的话).parallel将提供该功能.

您还传递了一个函数,以便在所有操作完成后调用.这将收到一个包含所有结果的数组.所以我的例子是:

refreshButton.click(function() {

  parallel([
       function(f) { 
         contentDiv.fadeOut(f); 
       },
       function(f) { 
         portlet.content(f); 
       },
     ], 
     function(results) {
      contentDiv.children().remove();
      contentDiv.append(results[1]);
      contentDiv.fadeIn();
  });
});
Run Code Online (Sandbox Code Playgroud)

因此,当我单击我的刷新按钮时,我会启动jQuery的fadeOut效果以及我自己的portlet.content函数(它执行异步get,构建新的内容并传递它),然后当两者都完成时我删除旧内容,追加结果第二个函数(在其中results[1])和fadeIn新内容.

由于fadeOut没有将任何内容传递给它的完成功能,results[0]可能包含undefined,所以我忽略它.但是如果你有三个有效结果的操作,它们将results按照你传递函数的顺序插入数组.


sri*_*_bb 7

并行运行多个AJAX请求

使用API​​时,有时需要向不同的端点发出多个AJAX请求.在发出下一个请求之前,不是等待一个请求完成,而是通过使用jQuery的$.when()函数并行请求数据来加快jQuery的速度:

JS

$.when($.get('1.json'), $.get('2.json')).then(function(r1, r2){
   console.log(r1[0].message + " " + r2[0].message);
});
Run Code Online (Sandbox Code Playgroud)

当这两个GET请求成功完成时,将执行回调函数.$ .when()接受两个$ .get()调用返回的promise,并构造一个新的promise对象.回调的r1和r2参数是数组,其第一个元素包含服务器响应.


agi*_*all 5

你可以做这样的事情

var allData = []
$.getJSON("/values/1", function(data) {
    allData.push(data);
    if(data.length == 2){
      processData(allData) // where process data processes all the data
    }
});

$.getJSON("/values/2", function(data) {
    allData.push(data);
    if(data.length == 2){
        processData(allData) // where process data processes all the data
    }
});

var processData = function(data){
     var sum = data[0] + data[1]
     $('#mynode').html(sum);
}
Run Code Online (Sandbox Code Playgroud)