Ajax 요청 시퀀싱
가끔 컬렉션을 반복하고 각 요소에 대해 아약스 호출을해야하는 경우가 있습니다. 다음 요소로 이동하기 전에 각 호출이 반환되기를 원하므로 서버에 요청이 발생하지 않도록해야합니다. 이로 인해 종종 다른 문제가 발생합니다. 그리고 비동기를 false로 설정하고 브라우저를 고정하고 싶지 않습니다.
일반적으로 여기에는 각 성공 콜백에 대해 단계별로 수행하는 일종의 반복자 컨텍스트 설정이 포함됩니다. 더 깨끗한 방법이 있어야한다고 생각합니다.
누구든지 각 항목에 대해 ajax 호출을하는 컬렉션을 통해 깔끔하게 작업하는 방법에 대한 영리한 디자인 패턴을 가지고 있습니까?
jQuery 1.5 이상
내가 개발 $.ajaxQueue()
를 사용하는 플러그인을 $.Deferred
, .queue()
그리고 $.ajax()
또 다시 전달하기 위해 약속을 할 때 요청이 완료 해결됩니다.
/*
* jQuery.ajaxQueue - A queue for ajax requests
*
* (c) 2011 Corey Frang
* Dual licensed under the MIT and GPL licenses.
*
* Requires jQuery 1.5+
*/
(function($) {
// jQuery on an empty object, we are going to use this as our Queue
var ajaxQueue = $({});
$.ajaxQueue = function( ajaxOpts ) {
var jqXHR,
dfd = $.Deferred(),
promise = dfd.promise();
// queue our ajax request
ajaxQueue.queue( doRequest );
// add the abort method
promise.abort = function( statusText ) {
// proxy abort to the jqXHR if it is active
if ( jqXHR ) {
return jqXHR.abort( statusText );
}
// if there wasn't already a jqXHR we need to remove from queue
var queue = ajaxQueue.queue(),
index = $.inArray( doRequest, queue );
if ( index > -1 ) {
queue.splice( index, 1 );
}
// and then reject the deferred
dfd.rejectWith( ajaxOpts.context || ajaxOpts,
[ promise, statusText, "" ] );
return promise;
};
// run the actual query
function doRequest( next ) {
jqXHR = $.ajax( ajaxOpts )
.done( dfd.resolve )
.fail( dfd.reject )
.then( next, next );
}
return promise;
};
})(jQuery);
jQuery 1.4
jQuery 1.4를 사용하는 경우 빈 개체의 애니메이션 대기열을 활용하여 요소에 대한 ajax 요청을위한 고유 한 "대기열"을 만들 수 있습니다.
이를 자신의 $.ajax()
교체에 고려할 수도 있습니다. 이 플러그인 $.ajaxQueue()
은 jQuery에 대한 표준 'fx'큐를 사용하며 큐가 아직 실행 중이 아닌 경우 처음 추가 된 요소를 자동으로 시작합니다.
(function($) {
// jQuery on an empty object, we are going to use this as our Queue
var ajaxQueue = $({});
$.ajaxQueue = function(ajaxOpts) {
// hold the original complete function
var oldComplete = ajaxOpts.complete;
// queue our ajax request
ajaxQueue.queue(function(next) {
// create a complete callback to fire the next event in the queue
ajaxOpts.complete = function() {
// fire the original complete if it was there
if (oldComplete) oldComplete.apply(this, arguments);
next(); // run the next query in the queue
};
// run the query
$.ajax(ajaxOpts);
});
};
})(jQuery);
사용 예
그래서 우리는 (ajax를 사용하여) 복사하고 싶은 것을 <ul id="items">
가지고 있습니다 <li>
.<ul id="output">
// get each item we want to copy
$("#items li").each(function(idx) {
// queue up an ajax request
$.ajaxQueue({
url: '/echo/html/',
data: {html : "["+idx+"] "+$(this).html()},
type: 'POST',
success: function(data) {
// Write to #output
$("#output").append($("<li>", { html: data }));
}
});
});
지연된 약속을 사용하는 빠르고 작은 솔루션입니다. 이것은 jQuery의을 사용하지만 $.Deferred
다른 모든 작업은 수행해야합니다.
var Queue = function () {
var previous = new $.Deferred().resolve();
return function (fn, fail) {
return previous = previous.then(fn, fail || fn);
};
};
사용법, 새 대기열 생성을위한 호출 :
var queue = Queue();
// Queue empty, will start immediately
queue(function () {
return $.get('/first');
});
// Will begin when the first has finished
queue(function() {
return $.get('/second');
});
비동기 요청을 나란히 비교 한 예 를 참조하세요 .
모든 복잡성을 함수로 래핑하여 다음과 같은 간단한 호출을 만들 수 있습니다.
loadSequantially(['/a', '/a/b', 'a/b/c'], function() {alert('all loaded')});
아래는 대략적인 스케치입니다 (Ajax 호출을 제외한 작업 예제). 배열 대신 큐와 같은 구조를 사용하도록 수정할 수 있습니다.
// load sequentially the given array of URLs and call 'funCallback' when all's done
function loadSequantially(arrUrls, funCallback) {
var idx = 0;
// callback function that is called when individual ajax call is done
// internally calls next ajax URL in the sequence, or if there aren't any left,
// calls the final user specified callback function
var individualLoadCallback = function() {
if(++idx >= arrUrls.length) {
doCallback(arrUrls, funCallback);
}else {
loadInternal();
}
};
// makes the ajax call
var loadInternal = function() {
if(arrUrls.length > 0) {
ajaxCall(arrUrls[idx], individualLoadCallback);
}else {
doCallback(arrUrls, funCallback);
}
};
loadInternal();
};
// dummy function replace with actual ajax call
function ajaxCall(url, funCallBack) {
alert(url)
funCallBack();
};
// final callback when everything's loaded
function doCallback(arrUrls, func) {
try {
func();
}catch(err) {
// handle errors
}
};
이상적으로는 서버의 모든 콜백이 동일한 코 루틴을 호출 할 수 있도록 여러 진입 점이있는 코 루틴이 깔끔 할 것입니다. 젠장, 이것은 Javascript 1.7에서 구현 될 예정입니다.
클로저를 사용해 보겠습니다 ...
function BlockingAjaxCall (URL,arr,AjaxCall,OriginalCallBack)
{
var nextindex = function()
{
var i =0;
return function()
{
return i++;
}
};
var AjaxCallRecursive = function(){
var currentindex = nextindex();
AjaxCall
(
URL,
arr[currentindex],
function()
{
OriginalCallBack();
if (currentindex < arr.length)
{
AjaxCallRecursive();
}
}
);
};
AjaxCallRecursive();
}
// suppose you always call Ajax like AjaxCall(URL,element,callback) you will do it this way
BlockingAjaxCall(URL,myArray,AjaxCall,CallBack);
예, 다른 답변은 작동하지만 코드가 많고 복잡해 보입니다. Frame.js는 이러한 상황을 우아하게 해결하도록 설계되었습니다. https://github.com/bishopZ/Frame.js
예를 들어, 이로 인해 대부분의 브라우저가 중단됩니다.
for(var i=0; i<1000; i++){
$.ajax('myserver.api', { data:i, type:'post' });
}
이것은 다음과 같지 않습니다.
for(var i=0; i<1000; i++){
Frame(function(callback){
$.ajax('myserver.api', { data:i, type:'post', complete:callback });
});
}
Frame.start();
또한 Frame을 사용하면 응답 객체를 폭포수 처리하고 전체 AJAX 요청이 완료된 후 (원하는 경우) 모두 처리 할 수 있습니다.
var listOfAjaxObjects = [ {}, {}, ... ]; // an array of objects for $.ajax
$.each(listOfAjaxObjects, function(i, item){
Frame(function(nextFrame){
item.complete = function(response){
// do stuff with this response or wait until end
nextFrame(response); // ajax response objects will waterfall to the next Frame()
$.ajax(item);
});
});
Frame(function(callback){ // runs after all the AJAX requests have returned
var ajaxResponses = [];
$.each(arguments, function(i, arg){
if(i!==0){ // the first argument is always the callback function
ajaxResponses.push(arg);
}
});
// do stuff with the responses from your AJAX requests
// if an AJAX request returned an error, the error object will be present in place of the response object
callback();
});
Frame.start()
I am posting this answer thinking that it might help other persons in future, looking for some simple solutions in the same scenario.
This is now possible also using the native promise support introduced in ES6. You can wrap the ajax call in a promise and return it to the handler of the element.
function ajaxPromise(elInfo) {
return new Promise(function (resolve, reject) {
//Do anything as desired with the elInfo passed as parameter
$.ajax({
type: "POST",
url: '/someurl/',
data: {data: "somedata" + elInfo},
success: function (data) {
//Do anything as desired with the data received from the server,
//and then resolve the promise
resolve();
},
error: function (err) {
reject(err);
},
async: true
});
});
}
Now call the function recursively, from where you have the collection of the elements.
function callAjaxSynchronous(elCollection) {
if (elCollection.length > 0) {
var el = elCollection.shift();
ajaxPromise(el)
.then(function () {
callAjaxSynchronous(elCollection);
})
.catch(function (err) {
//Abort further ajax calls/continue with the rest
//callAjaxSynchronous(elCollection);
});
}
else {
return false;
}
}
I use http://developer.yahoo.com/yui/3/io/#queue to get that functionality.
The only solutions I can come up with is, as you say, maintaining a list of pending calls / callbacks. Or nesting the next call in the previous callback, but that feels a bit messy.
You can achieve the same thing using then
.
var files = [
'example.txt',
'example2.txt',
'example.txt',
'example2.txt',
'example.txt',
'example2.txt',
'example2.txt',
'example.txt'
];
nextFile().done(function(){
console.log("done",arguments)
});
function nextFile(text){
var file = files.shift();
if(text)
$('body').append(text + '<br/>');
if(file)
return $.get(file).then(nextFile);
}
http://plnkr.co/edit/meHQHU48zLTZZHMCtIHm?p=preview
I would suggest a bit more sophisticated approach which is reusable for different cases.
I am using it for example when I need to slow down a call sequence when the user is typing in text editor.
But I am sure it should also work when iterating through the collection. In this case it can queue requests and can send a single AJAX call instead of 12.
queueing = {
callTimeout: undefined,
callTimeoutDelayTime: 1000,
callTimeoutMaxQueueSize: 12,
callTimeoutCurrentQueueSize: 0,
queueCall: function (theCall) {
clearTimeout(this.callTimeout);
if (this.callTimeoutCurrentQueueSize >= this.callTimeoutMaxQueueSize) {
theCall();
this.callTimeoutCurrentQueueSize = 0;
} else {
var _self = this;
this.callTimeout = setTimeout(function () {
theCall();
_self.callTimeoutCurrentQueueSize = 0;
}, this.callTimeoutDelayTime);
}
this.callTimeoutCurrentQueueSize++;
}
}
참고URL : https://stackoverflow.com/questions/3034874/sequencing-ajax-requests
'Programing' 카테고리의 다른 글
jQuery에 요소가 있는지 정말로 확인해야합니까? (0) | 2020.11.27 |
---|---|
Thread.sleep (0) 및 Thread.yield () 문이 동일합니까? (0) | 2020.11.27 |
SSH를 통해 Mercurial 저장소 복제 (0) | 2020.11.27 |
여러 div를 한 줄에 표시하지만 여전히 너비를 유지하는 방법은 무엇입니까? (0) | 2020.11.27 |
Entity Framework 4.0 : 오류 113 : 다중성이 역할에서 유효하지 않습니다. (0) | 2020.11.27 |