私は初めてjQuery.queue()を使用していますが、まだ把握していません。誰かが私が間違っていることを指摘してもらえますか?
Firebugを見ると、まだ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
}
});
});
});
同様のことをする必要があったので、ここに自分の解決策を投稿すると思いました。
基本的に私が手に入れたのは、すべてに特有の基準がある棚のプロジェクトをリストするページです。残りのコンテンツが読み込まれている間に見ることができるコンテンツをユーザーにすばやく届けるために、棚を全部ではなく1つずつ読み込みたいと思いました。
基本的に、PHPから呼び出すときに使用するJS配列に各シェルフのIDを保存しました。
次に、呼び出されるたびに配列から最初のインデックスをポップし、ポップされた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
上記のソリューションはやや複雑であることがわかりました。さらに、送信する直前にリクエストを変更する必要がありました(新しいデータトークンを更新するため)。
だから私はこれをまとめた。ソース: 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);
}
};
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オプションを使用できます。
未知の数のAjax呼び出しに対してこれを行う必要がありました。答えは、それぞれを配列にプッシュしてから使用することでした:
$.when.apply($, arrayOfDeferreds).done(function () {
alert("All done");
});
タイマーなしの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 Webサイト 良い例もあります :
// 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
}));
}
});
});
nodejs用に作成したマルチスレッドキューランナーのもう1つの例です。 jqueryまたはangularに適応させることができます。約束は各APIでわずかに異なります。複数のクエリを作成してすべてのデータを取得し、一度に6を許可して、サーバーが課す調整制限を回避することにより、SharePointの大きなリストからすべてのアイテムを抽出するなどの目的でこのパターンを使用しました。
/*
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;
}());
また、私が持っていたソリューション内でこれを行う必要があり、私はこの方法でそれを行うことができることがわかりました:
_//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のハンドラーにnextを配置することで、ブラウザーとブラウザーのレンダリングまたはペイントスレッドに対してajax呼び出しを事実上非同期にしますが、互いに同期またはシリアル化されます。
これは非常に基本的な例です。 例のフィドル。ボタンを1回クリックして、2秒間待ちます。タイムアウトがトリガーされ、単一の操作が発生することがわかります。次にボタンをできるだけ早くクリックし(または1秒以上)、ボタンをクリックするたびに操作がキューに入れられ、1秒待ってからページにヒットし、1秒後にフェードインすることがわかりますもう一方。
これの利点は、キューが既に空になっている場合、空になっている間にキューに追加した操作は最後に配置され、時間が来たときに処理されることです。
knockout.js などの監視可能なサポートを提供するフレームワークを使用すると、プッシュされると呼び出しをキューに入れ、シフトがプロセスを処理する監視キューを実装できます。
ノックアウトの実装は次のようになります。
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);
});
}
});
別のajaxリクエストを送信するタイミングを通知するObservableを利用することに注意してください。このメソッドは、より一般化された形式で適用できます。
例として、多数のエントリを取得するノックアウトマッピングがあり、アイテムごとに別のサービスを呼び出して値を設定する必要があると想像してください。
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);
});
});
これが私のソリューションです。これを使用して、Browsergameのリクエストのキューを作成します。何か発生した場合は、このキューを停止し、特別な最後の要求またはクリーンアップで作業を終了します。
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();