Вопрос по jquery, ajax, javascript – Параллельные асинхронные запросы Ajax с использованием jQuery

69

Я хотел бы обновить страницу на основе результатов нескольких запросов 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);
  });

});

Однако это приводит к тому, что запросы выполняются последовательно. Я предпочел бы способ делать запросы параллельно и выполнять обновление страницы после того, как все будет завершено. Есть какой-либо способ сделать это?

Ваш Ответ

13   ответов
9

Update: Согласно ответу Яира Левиеля, этот ответ устарел. Используйте библиотеку обещаний, такую как jQuery.when () или Q.js.

Я создал решение общего назначения как расширение jQuery. Могли бы использовать тонкую настройку, чтобы сделать ее более общей, но она отвечала моим потребностям. Преимущество этого метода перед другими в этой публикации на момент написания этой статьи было то, чтоany Тип асинхронной обработки с обратным вызовом может быть использован.

Примечание. Я бы использовал расширения Rx для JavaScript вместо этого, если бы думал, что мой клиент будет в порядке, если возьмет зависимость от еще одной сторонней библиотеки :)

// 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);
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
0

var templateNameArray=["test.html","test2.html","test3.html"];

htmlTemplatesLoadStateMap={};
var deffereds=[];
  for (var i = 0; i < templateNameArray.length; i++)
       {
        if (!htmlTemplatesLoadStateMap[templateNameArray[i]]) 
            {         
              deferreds.push($.get("./Content/templates/" +templateNameArray[i], 

                  function (response, status, xhr) {
                      if (status == "error") { } 
                        else {
                                $("body").append(response);
                               }
                         }));             
htmlTemplatesLoadStateMap[templateNameArray[i]] = true;
                       }
                  }
                                      $.when.all(deferreds).always(function(resultsArray) {   yourfunctionTobeExecuted(yourPayload);
                                });
1

вы не можете сделать их параллельными.

Один не зависит от другого, но конечный результат зависит от того, что завершено. Paul
Error: User Rate Limit Exceeded
8

По сути, вы просто создаете и вызываете 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>

здесь test.php

<?php

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

?>
Оо зашел слишком далеко.
1

Вы можете определить ajax-обещания динамически.

var start = 1; // starting value
var len = 2; // no. of requests

var promises = (new Array(len)).fill().map(function() {
    return $.ajax("/values/" + i++);
});

$.when.apply($, promises)
  .then(myFunc, myFailure);
100

которое может поддерживать любое конкретное количество параллельных запросов:

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);
  });
});
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededgist.github.com/137804 Paul
Error: User Rate Limit Exceeded Paul
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
7

Run multiple AJAX requests in parallel

При работе с API иногда необходимо выполнить несколько запросов AJAX для разных конечных точек. Вместо того чтобы ждать выполнения одного запроса перед тем, как выдать следующий, вы можете ускорить процесс с помощью jQuery, запросив данные параллельно, используя jQuery.$.when() функция:

JS

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

Функция обратного вызова выполняется, когда оба этих запроса GET завершаются успешно. $ .when () принимает обещания, возвращенные двумя вызовами $ .get (), и создает новый объект обещания. Аргументы обратного вызова r1 и r2 - это массивы, первые элементы которых содержат ответы сервера.

3

async.js и Array.reduce вроде так:

        async.map([1, 2, 3, 4, 5], function (number, callback) {
            $.getJSON("/values/" + number, function (data) {
                callback(null, data.value);
            });
        }, function (err, results) {
            $("#mynode").html(results.reduce(function(previousValue, currentValue) {
                return previousValue + currentValue;
            }));
        });
109

$ .When () а также$ .Done () это именно то, что вам нужно:

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceededcodepen.io/jacobgoh101/pen/YaJOzx?editors=0010
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
Error: User Rate Limit Exceeded
3

функция, вы можете сделать это:

$.whenAll({
    val1: $.getJSON('/values/1'),
    val2: $.getJSON('/values/2')
})
    .done(function (results) {
        var sum = results.val1.value + results.val2.value;

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

Расширение JQuery (1.x) whenAll ():

$.whenAll = function (deferreds) {
    function isPromise(fn) {
        return fn && typeof fn.then === 'function' &&
            String($.Deferred().then) === String(fn.then);
    }
    var d = $.Deferred(),
        keys = Object.keys(deferreds),
        args = keys.map(function (k) {
            return $.Deferred(function (d) {
                var fn = deferreds[k];

                (isPromise(fn) ? fn : $.Deferred(fn))
                    .done(d.resolve)
                    .fail(function (err) { d.reject(err, k); })
                ;
            });
        });

    $.when.apply(this, args)
        .done(function () {
            var resObj = {},
                resArgs = Array.prototype.slice.call(arguments);
            resArgs.forEach(function (v, i) { resObj[keys[i]] = v; });
            d.resolve(resObj);
        })
        .fail(d.reject);

    return d;
};

Смотрите пример jsbin: http://jsbin.com/nuxuciwabu/edit?js,console

3

mbostock / очереди:

queue()
  .defer(function(callback) {
    $.post('/echo/json/', {json: JSON.stringify({value: 1}), delay: 1}, function(data) {
      callback(null, data.value);
    });
  })
  .defer(function(callback) {
    $.post('/echo/json/', {json: JSON.stringify({value: 3}), delay: 2}, function(data) {
      callback(null, data.value);
    });
  })
  .awaitAll(function(err, results) {
    var result = results.reduce(function(acc, value) {
      return acc + value;
    }, 0);
    console.log(result);
  });

Связанная скрипка:http://jsfiddle.net/MdbW2/

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

UPDATE И еще два года спустя это выглядит безумно, потому что принятый ответ изменился на что-то намного лучшее! (Хотя все еще не так хорошо, как ответ Яир Левиэль, используя jQuery'swhen)

18 месяцев спустя я просто ударил что-то подобное. У меня есть кнопка обновления, и я хочу, чтобы старый контентfadeOut а затем новый контентfadeIn, Но мне тоже нужноget новый контент.fadeOut иget являются асинхронными, но было бы пустой тратой времени на их последовательный запуск.

То, что я делаю, на самом деле совпадает с принятым ответом, за исключением использования в качестве функции многократного использования. Его главное достоинство заключается в том, что он намного короче, чем другие предложения здесь.

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

Вы передаете ему массив функций для параллельного запуска. Каждая функция должна принимать другую функцию, которой она передает свой результат (если есть).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();
  });
});

Поэтому, когда нажимается моя кнопка обновления, я запускаю jQueryfadeOut эффект, а также мой собственныйportlet.content функция (которая делает асинхронныйget, создает новый бит контента и передает его), а затем, когда оба завершены, я удаляю старый контент, добавляю результат второй функции (которая находится вresults[1]) а такжеfadeIn новый контент.

КакfadeOut ничего не передает своей функции завершения,results[0] предположительно содержитundefinedтак что я игнорирую это. Но если бы у вас было три операции с полезными результатами, они бы каждый слот вresults массив, в том же порядке вы передали функции.

Похожие вопросы