programing

jQuery.queue ()를 사용하여 Ajax 요청 대기열

copysource 2021. 1. 17. 11:51
반응형

jQuery.queue ()를 사용하여 Ajax 요청 대기열


나는 처음으로 jQuery.queue ()를 사용하고 있으며 그것을 잘 이해하지 못했습니다. 누군가 내가 뭘 잘못하고 있는지 지적 해 주시겠습니까?

방화범을 살펴보면 POST 요청이 동시에 실행되는 것을 볼 수 있으므로 잘못된 위치에서 dequeue ()를 호출하고 있는지 궁금합니다.

또한-대기열 길이를 어떻게 얻을 수 있습니까?

이러한 요청을 대기열에 넣어야하는 이유는 버튼 클릭시 실행되기 때문입니다. 그리고 사용자가 여러 버튼을 빠르게 연속으로 클릭 할 수 있습니다.

내 코드의 기본 구조를 제거하려고 시도했습니다.

$("a.button").click(function(){
   $(this).doAjax(params);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       params: params,
       success: function(data){
         doStuff;

         $(document).dequeue("myQueueName");
       }
     });
   });

}

여기서 문제 .ajax()는 비동기 실행 Ajax 요청을 실행 한다는 것 입니다. 즉, .ajax()즉시 반환되며 차단되지 않습니다. 따라서 대기열에 함수가 있지만 설명한 것처럼 거의 동시에 실행됩니다.

나는 .queue()ajax 요청을 받기에 좋은 곳 이라고 생각하지 않으며 fx methods. 간단한 관리자가 필요합니다.

var ajaxManager = (function() {
     var requests = [];

     return {
        addReq:  function(opt) {
            requests.push(opt);
        },
        removeReq:  function(opt) {
            if( $.inArray(opt, requests) > -1 )
                requests.splice($.inArray(opt, requests), 1);
        },
        run: function() {
            var self = this,
                oriSuc;

            if( requests.length ) {
                oriSuc = requests[0].complete;

                requests[0].complete = function() {
                     if( typeof(oriSuc) === 'function' ) oriSuc();
                     requests.shift();
                     self.run.apply(self, []);
                };   

                $.ajax(requests[0]);
            } else {
              self.tid = setTimeout(function() {
                 self.run.apply(self, []);
              }, 1000);
            }
        },
        stop:  function() {
            requests = [];
            clearTimeout(this.tid);
        }
     };
}());

이것은 완벽 함과는 거리가 멀다. 나는 단지가는 길을 보여주고 싶다. 위의 예는 다음과 같은 방식으로 사용될 수 있습니다.

$(function() {
    ajaxManager.run(); 

    $("a.button").click(function(){
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

비슷한 일을해야 했으므로 여기에 솔루션을 게시 할 것이라고 생각했습니다.

기본적으로 내가 가진 것은 모두 고유 한 기준을 가진 선반에 프로젝트를 나열하는 페이지입니다. 나머지는로드하는 동안 사용자가 볼 수있는 일부 콘텐츠를 사용자에게 더 빨리 제공하기 위해 전체가 아닌 하나씩 선반을로드하고 싶었습니다.

기본적으로 각 선반의 ID를 PHP에서 호출 할 때 사용하는 JS 배열에 저장했습니다.

그런 다음 호출 될 때마다 배열에서 첫 번째 인덱스를 팝하고 팝된 ID에 대한 선반을 요청하는 재귀 함수를 만들었습니다. 에서 응답을 받았 $.get()거나 $.post()선호하는 것을 사용하면 콜백 내에서 재귀 함수를 호출합니다.

다음은 코드에 대한 정교함입니다.

// array of shelf IDs
var shelves = new Array(1,2,3,4);

// the recursive function
function getShelfRecursive() {

    // terminate if array exhausted
    if (shelves.length === 0)
        return;

    // pop top value
    var id = shelves[0];
    shelves.shift();

    // ajax request
    $.get('/get/shelf/' + id, function(){
         // call completed - so start next request
         getShelfRecursive();
    });
}

// fires off the first call
getShelfRecursive();

이 매우 간단한 코드를 사용하여 ajax 호출이 서로 "추월"하지 못하도록합니다.

var dopostqueue = $({});
function doPost(string, callback)
{
    dopostqueue.queue(function()
    {
        $.ajax(
        {   
            type: 'POST',
            url: 'thephpfile.php',
            datatype: 'json',
            data: string,
            success:function(result) 
            {
                dopostqueue.dequeue();
                callback(JSON.parse(result));
            }
        })
    });
}

대기열이 자체적으로 처리하지 않도록 dequeue하려면 함수에서를 제거하고 다른 함수에서 호출하면됩니다. 큐 길이를 가져 오는 방법은 다음과 같습니다.

dopostqueue.queue().length

알 수없는 수의 ajax 호출에 대해이 작업을 수행해야했습니다. 대답은 각각을 배열에 넣은 다음 다음을 사용하는 것입니다.

$.when.apply($, arrayOfDeferreds).done(function () {
    alert("All done");
});

jQuery를 확장 할 수 있습니다.

(function($) {
  // 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);

다음과 같이 사용하십시오.

$.ajaxQueue({
    url: 'doThisFirst.php',
    async: true,
    success: function (data) {
        //success handler
    },
    error: function (jqXHR,textStatus,errorThrown) {
        //error Handler
    }       
});
$.ajaxQueue({
    url: 'doThisSecond.php',
    async: true,
    success: function (data) {
        //success handler
    },
    error: function (jqXHR,textStatus,errorThrown) {
        //error Handler
    }       
});

물론 $ .ajax를 확장하므로 type, data, contentType, DataType과 같은 다른 $ .ajax 옵션을 사용할 수 있습니다.


위의 솔루션이 다소 복잡하다는 것을 알게되었고 전송 직전에 요청을 변경해야했습니다 (새로운 데이터 토큰을 업데이트하기 위해).

그래서 저는 이것을 하나로 합쳤습니다. 출처 : https://gist.github.com/2470554

/* 

Allows for ajax requests to be run synchronously in a queue

Usage::

var queue = new $.AjaxQueue();

queue.add({
  url: 'url',
  complete: function() {
    console.log('ajax completed');
  },
  _run: function(req) {
    //special pre-processor to alter the request just before it is finally executed in the queue
    req.url = 'changed_url'
  }
});

*/

$.AjaxQueue = function() {
  this.reqs = [];
  this.requesting = false;
};
$.AjaxQueue.prototype = {
  add: function(req) {
    this.reqs.push(req);
    this.next();
  },
  next: function() {
    if (this.reqs.length == 0)
      return;

    if (this.requesting == true)
      return;

    var req = this.reqs.splice(0, 1)[0];
    var complete = req.complete;
    var self = this;
    if (req._run)
      req._run(req);
    req.complete = function() {
      if (complete)
        complete.apply(this, arguments);
      self.requesting = false;
      self.next();
    }

    this.requesting = true;
    $.ajax(req);
  }
};

타이머가없는 jAndy의 답변의 또 다른 버전.

var ajaxManager = {
    requests: [],
    addReq: function(opt) {
        this.requests.push(opt);

        if (this.requests.length == 1) {
            this.run();
        }
    },
    removeReq: function(opt) {
        if($.inArray(opt, requests) > -1)
            this.requests.splice($.inArray(opt, requests), 1);
    },
    run: function() {
        // original complete callback
        oricomplete = this.requests[0].complete;

        // override complete callback
        var ajxmgr = this;
        ajxmgr.requests[0].complete = function() {
             if (typeof oricomplete === 'function')
                oricomplete();

             ajxmgr.requests.shift();
             if (ajxmgr.requests.length > 0) {
                ajxmgr.run();
             }
        };

        $.ajax(this.requests[0]);
    },
    stop: function() {
        this.requests = [];
    },
}

쓰다:

$(function() {
    $("a.button").click(function(){
       ajaxManager.addReq({
           type: 'POST',
           url: 'whatever.html',
           data: params,
           success: function(data){
              // do stuff
           }
       });
    });
});

learn.jquery.com 웹 사이트 에도 좋은 예가 있습니다 .

// 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 invoke the next event in the queue
    ajaxOpts.complete = function() {
      // Invoke the original complete if it was there
      if (oldComplete) {
        oldComplete.apply(this, arguments);
      }

      // Run the next query in the queue
      next();
    };

    // Run the query
    $.ajax(ajaxOpts);
  });
};

// Get each item we want to copy
$("#items li").each(function(idx) {
  // Queue up an ajax request
  $.ajaxQueue({
    url: "/ajax_html_echo/",
    data: {
      html: "[" + idx + "] " + $(this).html()
    },
    type: "POST",
    success: function(data) {
      // Write to #output
      $("#output").append($("<li>", {
        html: data
      }));
    }
  });
});

나는 또한 내가 가진 솔루션 내에서 이것을해야했고 다음과 같이 할 수 있음을 알았습니다.

//A variable for making sure to wait for multiple clicks before emptying.
var waitingTimeout; 

$("a.button").click(function(){
   $(this).doAjax(params);
   clearTimeout(waitingTimeout);
   waitingTimeout = setTimeout(function(){noMoreClicks();},1000);
});

// method
doAjax:function(params){ 

   $(document).queue("myQueueName", function(next){
     $.ajax({
       type: 'POST',
       url: 'whatever.html',
       data: params,
       contentType: "application/json; charset=utf-8",
       dataType: "json",
       success: function(data){
         doStuff;
         next();
       },
       failure: function(data){
         next();
       },
       error: function(data){
         next();
       }
     });
   });

}

function noMoreClicks(){
    $(document).dequeue("myQueueName");
}

next()대기열 함수에서 전달 된 콜백을 사용하여 다음 작업을 대기열에서 빼낼 수 있습니다. 따라서 다음을 ajax의 핸들러에 넣으면 브라우저와 브라우저의 렌더 또는 페인트 스레드에 대해 비동기식으로 ajax 호출을 효과적으로 수행 할 수 있지만 서로 동기 또는 직렬화됩니다.

다음은 매우 기본적인 예입니다. 예제 바이올린에서. 버튼을 한 번 클릭하고 잠시 기다리십시오. 시간 초과가 트리거되고 단일 작업이 발생하는 것을 볼 수 있습니다. 다음으로 가능한 한 빨리 (또는 1 초 이상) 버튼을 클릭하면 버튼을 클릭 할 때마다 작업이 대기열에 들어간 다음 잠시 기다린 후에 만 ​​페이지에 도달하고 1 초 후에 페이드 인되는 것을 볼 수 있습니다. 다른 하나.

이것의 장점은 큐가 이미 비우고있는 경우 비우는 동안 추가 한 모든 작업이 끝에 배치 된 다음 시간이되면 처리된다는 것입니다.


다음은 일부 브라우저 게임에 대한 요청 대기열을 생성하는 데 사용하는 솔루션입니다. 어떤 일이 발생하면이 대기열을 중지하고 특별한 마지막 요청 또는 정리 작업을 완료합니다.

var get_array = ["first", "second", "third"];

var worker = $("<div />"); // to line up requests in queue
$.queuedAjax = function(args){  // add up requests for me       
    worker.queue(
        function(next){
            $.ajax(args).always(next);            
        }
    );
  };

$.queuedSomething = function(){ // add up something special for me
    worker.queue(
        function(next){
            //worker.clearQueue();
            //worker = $("<div />"); //cleanup for next .each
            //maybe another .each           
        }
    );
  };

$.each( get_array , function( key , value ) {
  $.queuedAjax({
    type: 'GET',
    url: '/some.php?get='+value,
    dataType: 'text',
    success: function(sourcecode){

        if (sourcecode.match(/stop your requests, idiot!/)) {   
            worker.clearQueue().queue($.queuedSomething);
            alert(' the server told me to stop. i stopped all but not the last ´$.queuedSomething()´ ');
        }

    }
  });           
}); 
$.queuedSomething();

nodejs에 대해 작성한 다중 스레드 큐 러너의 또 다른 예입니다. jquery 또는 angular에 맞게 조정할 수 있습니다. 약속은 API마다 약간 다릅니다. 저는이 패턴을 SharePoint의 큰 목록에서 모든 항목을 추출하여 모든 데이터를 가져 오는 여러 쿼리를 만들고 한 번에 6 개를 허용하여 서버에서 부과하는 제한을 방지하는 등의 작업에 사용했습니다.

/*
    Job Queue Runner (works with nodejs promises): Add functions that return a promise, set the number of allowed simultaneous threads, and then run
    (*) May need adaptation if used with jquery or angular promises

    Usage:
        var sourcesQueue = new QueueRunner('SourcesQueue');
        sourcesQueue.maxThreads = 1;
        childSources.forEach(function(source) {
            sourcesQueue.addJob(function() { 
                // Job function - perform work on source
            });
        }
        sourcesQueue.run().then(function(){
            // Queue complete...
        });
*/
var QueueRunner = (function () {
    function QueueRunner(id) {
        this.maxThreads = 1; // Number of allowed simultaneous threads
        this.jobQueue = [];
        this.threadCount = 0;
        this.jobQueueConsumer = null;
        this.jobsStarted = 0;
        if(typeof(id) !== 'undefined') {
            this.id = id;
        }
        else {
            this.id = 'QueueRunner';
        }
    }    
    QueueRunner.prototype.run = function () {
        var instance = this;        
        return new Promise(function(resolve, reject) {
            instance.jobQueueConsumer = setInterval(function() {
                if(instance.threadCount < instance.maxThreads && instance.jobQueue.length > 0) {
                    instance.threadCount++;
                    instance.jobsStarted++;
                    // Remove the next job from the queue (index zero) and run it
                    var job = instance.jobQueue.splice(0, 1)[0];
                    logger.info(instance.id + ': Start job ' + instance.jobsStarted + ' of ' + (instance.jobQueue.length + instance.jobsStarted));
                    job().then(function(){
                        instance.threadCount--;
                    }, function(){
                        instance.threadCount--;
                    });
                }
                if(instance.threadCount < 1 && instance.jobQueue.length < 1) {
                    clearInterval(instance.jobQueueConsumer);
                    logger.info(instance.id + ': All jobs done.');
                    resolve();
                }
            }, 20);
        });     
    };
    QueueRunner.prototype.addJob = function (func) {
        this.jobQueue.push(func);
    };
    return QueueRunner;
}());

Using a framework which provides observable support such as knockout.js you can implement an observing queue which when pushed onto will enqueue the call and a shift will process the process.

A knockout implementation would look like the following:

var ajaxQueueMax = 5;
self.ajaxQueue = ko.observableArray();
self.ajaxQueueRunning = ko.observable(0);

ko.computed(function () {
  if (self.ajaxQueue().length > 0 && self.ajaxQueueRunning() < ajaxQueueMax) {
    var next = self.ajaxQueue.shift();
    self.ajaxQueueRunning(self.ajaxQueueRunning() + 1);
    $.ajax(next).always(function () {
      self.ajaxQueueRunning(self.ajaxQueueRunning() - 1);
    });
  }
});

Observe that we take advantage of the observables telling us when we should send off another ajax request. This method can be applied in a more generalised form.

As an example, imagine you had a knockout mapping that retrieved lots of entries but you needed to call another service per item to enrich them, say set a value.

self.widgets = ko.observableArray();

ko.computed(function () {
  var mapping = {
    create: function (options) {
      var res = ko.mapping.fromJS(options.data);
      res.count = ko.observable();

      // widget enrichment.
      self.ajaxQueue.push({
        dataType: "json",
        url: "/api/widgets/" + options.data.id + "/clicks",
        success: function (data) {
          res.count(data);
        }
      });
      return res;
    }
  };

  // Initial request for widgets
  $.getJSON("/api/widgets", function (data) {
    ko.mapping.fromJS(data, mapping, self.widgets);
  });
});

ReferenceURL : https://stackoverflow.com/questions/4785724/queue-ajax-requests-using-jquery-queue

반응형