program story

jQuery를 사용한 병렬 비동기 Ajax 요청

inputbox 2020. 10. 25. 12:11
반응형

jQuery를 사용한 병렬 비동기 Ajax 요청


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

});

그러나 이로 인해 요청이 순차적으로 이루어집니다. 나는 요청을 병렬로 만들고 모든 것이 완료된 후에 페이지 업데이트를 수행하는 방법을 선호합니다. 이것을 할 방법이 있습니까?


특정 수의 병렬 쿼리를 지원할 수있는이 솔루션을 사용해보십시오.

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

jQuery $ .when ()$ .done () 은 정확히 필요한 것입니다.

$.when($.ajax("/page1.php"), $.ajax("/page2.php"))
  .then(myFunc, myFailure);

귀하의 질문에 직접 답변하려는 시도는 다음과 같습니다.

기본적으로 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 );

?>

업데이트 : Yair Leviel이 제공 한 답변에 따르면이 답변은 구식입니다. jQuery.when () 또는 Q.js와 같은 promise 라이브러리를 사용하세요.


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

업데이트 그리고 2 년 후, 받아 들여진 대답이 훨씬 더 나은 것으로 바뀌었기 때문에 이것은 미쳐 보입니다! (JQuery를 사용하는 Yair Leviel의 답변만큼 좋지는 않지만 when)

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

그래서 내 새로 고침 버튼을 클릭하면 jQuery의 fadeOut효과와 내 자신의 portlet.content기능 (async get를 수행하고 새로운 콘텐츠를 빌드하고 전달 함)을 시작한 다음 둘 다 완료되면 이전 콘텐츠를 제거하고 결과를 추가합니다. 두 번째 함수 (에 있음 results[1]) 및 fadeIn새 콘텐츠의

으로는 fadeOut, 완성 기능에 아무것도 전달하지 않습니다 results[0]아마도이 들어 undefined나는 그것을 무시, 그래서. 그러나 유용한 결과를 가진 세 가지 작업이있는 results경우 함수를 전달한 것과 동일한 순서 로 각 슬롯이 배열에 들어갑니다 .


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

이러한 GET 요청이 모두 성공적으로 완료되면 콜백 함수가 실행됩니다. $ .when ()은 두 개의 $ .get () 호출에 의해 반환 된 promise를 취하고 새로운 promise 객체를 생성합니다. 콜백의 r1 및 r2 인수는 첫 번째 요소에 서버 응답이 포함 된 배열입니다.


이런 식으로 할 수 있습니다

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

다음은 mbostock / queue를 사용한 구현입니다 .

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/


With the following extension of JQuery (to can be written as a standalone function you can do this:

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

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

The JQuery (1.x) extension 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;
};

See jsbin example: http://jsbin.com/nuxuciwabu/edit?js,console


The most professional solution for me would be by using async.js and Array.reduce like so:

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

If the result of one request depends on the other, you can't make them parallel.


Building on Yair's answer. You can define the ajax promises dynamically.

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

Suppose you have an array of file name.

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

참고URL : https://stackoverflow.com/questions/1060539/parallel-asynchronous-ajax-requests-using-jquery

반응형