服务器仍在处理上一个请求时如何排队 ajax 请求?
How to queue ajax request while the server is still processing previous request?
当用户在浏览器中输入时,我正在执行实时服务器端搜索。我的搜索功能跟不上打字速度,所以请求堆积如山。
我希望能够在服务器准备好响应之前取消被新查询替换的查询。
这就是我想要实现的:
- 第一次击键应该发送到服务器。
- 下一次击键应由客户端脚本排队,直到服务器响应第一个查询。
- 如果在此期间发生另一个击键,它应该替换当前排队的击键。
这样,一旦服务器准备就绪,只有最近的击键才会发送到服务器。当然,如果没有队列,直接发送一个击键到服务器即可。
我当前的实现非常简单:
$.ajax({
type: "get",
dataType: "script",
data: {
search: "something"
},
url: "/api/search",
success: function(data, status, xhr) {},
error: function(xhr, textStatus, errorThrown) {}
});
var req = $.ajax();
//kill the request
req.abort()
您可以使用 $.ajax.abort 方法终止请求。在每次按键时,终止前一个请求(如果有的话)。
var prevReq = {};
elem.onkeyup = function(){
if(prevReq.abort)
prevReq.abort();
var prevReq = $.ajax(/*make the api call and clear prevReq object on response*/);
};
var arr = [], // bufor
flag = true;
window.onkeydown = function (e) {
arr.push(e);
sync();
}
function sync() {
if(flag){
flag = false;
sendAjax(arr.shift());
}
setTimeout(function () {
sync();
}, 40)
}
但是在 sendAjax 中做类似的事情:
.onSuccess = function () {flag = true;}
.onError = function () {flag = true;}
它很原始,但你可以开发它,在这个阶段它比功能更灵活。
"But it works, and things that work - arent stupid":)
我认为您的方法会淹没服务器,因为您在每次击键时都会向服务器发送请求。即使您像@SravanS 所说的那样在客户端中止请求,服务器仍将接收并开始处理所有这些请求。发送的 HTTP 请求已经在网上,你无法阻止它,你可以做的是没有客户端中止它,我假设通知服务器或只是忽略它发送的响应,但你仍然在泛滥它。
也许实施延迟以识别用户何时停止输入是最好的方法。这是一个通用的延迟事件处理程序工厂,非常易于使用,只需将您的函数传递给它并为其分配一个延迟。如果击键之间经过 X 毫秒,将发送请求,但如果在该延迟之前发生另一次击键,您甚至不会发出请求。
function delayedEvent(eventHandler, delay) {
var lastTimeout = null;
return function () {
var that = this,
args= Array.prototype.slice.call(arguments).sort();
if (lastTimeout !== null)
window.clearTimeout(lastTimeout);
lastTimeout = window.setTimeout(function () {
eventHandler.apply(that, args);
}, delay);
};
}
$('... selector ...').on('event', delayedEvent(function () { ... your code ... }, 500));
编辑:这就是实现队列的方法,我还没有测试过这段代码,请以它为起点。
function eventQueue(requestMaker) {
// Here we store the last event queued, it'll wait until all the already running events are done.
var lastQueued = null,
runningQueue = [];
// Push a new event into the running queue
function pushRunning(toPush) {
runningQueue.push(toPush.done(
// It is necesary to use an IIFE to get the index by value and not by reference
(function (index) {
return function() {
// Remove this event from the runningqueue
runningQueue.splice(index, 1);
// If the queue has been completed, start running the last event sent
if (lastQueued !== null && runningQueue.length === 0) {
pushRunning(lastQueued());
}
}
}(runningQueue.lenght));
));
}
return function () {
var that = this,
args = Array.prototype.slice.call(arguments).sort();
// Some events are already running, just override the lastQueued
if (runningQueue.length > 0) {
lastQueued = requestMaker.bind(that, args);
} else {
// Nothing queued run this event straight away, and queue it as running
pushRunning(requestMaker.apply(that, args));
}
};
}
$('... selector ...').on('event', eventQueue(function () {
// It's extremely important that you return the jquery $.ajax Promise
return $.ajax(...);
}, 500));
当用户在浏览器中输入时,我正在执行实时服务器端搜索。我的搜索功能跟不上打字速度,所以请求堆积如山。
我希望能够在服务器准备好响应之前取消被新查询替换的查询。
这就是我想要实现的:
- 第一次击键应该发送到服务器。
- 下一次击键应由客户端脚本排队,直到服务器响应第一个查询。
- 如果在此期间发生另一个击键,它应该替换当前排队的击键。
这样,一旦服务器准备就绪,只有最近的击键才会发送到服务器。当然,如果没有队列,直接发送一个击键到服务器即可。
我当前的实现非常简单:
$.ajax({
type: "get",
dataType: "script",
data: {
search: "something"
},
url: "/api/search",
success: function(data, status, xhr) {},
error: function(xhr, textStatus, errorThrown) {}
});
var req = $.ajax();
//kill the request
req.abort()
您可以使用 $.ajax.abort 方法终止请求。在每次按键时,终止前一个请求(如果有的话)。
var prevReq = {};
elem.onkeyup = function(){
if(prevReq.abort)
prevReq.abort();
var prevReq = $.ajax(/*make the api call and clear prevReq object on response*/);
};
var arr = [], // bufor
flag = true;
window.onkeydown = function (e) {
arr.push(e);
sync();
}
function sync() {
if(flag){
flag = false;
sendAjax(arr.shift());
}
setTimeout(function () {
sync();
}, 40)
}
但是在 sendAjax 中做类似的事情:
.onSuccess = function () {flag = true;}
.onError = function () {flag = true;}
它很原始,但你可以开发它,在这个阶段它比功能更灵活。
"But it works, and things that work - arent stupid":)
我认为您的方法会淹没服务器,因为您在每次击键时都会向服务器发送请求。即使您像@SravanS 所说的那样在客户端中止请求,服务器仍将接收并开始处理所有这些请求。发送的 HTTP 请求已经在网上,你无法阻止它,你可以做的是没有客户端中止它,我假设通知服务器或只是忽略它发送的响应,但你仍然在泛滥它。
也许实施延迟以识别用户何时停止输入是最好的方法。这是一个通用的延迟事件处理程序工厂,非常易于使用,只需将您的函数传递给它并为其分配一个延迟。如果击键之间经过 X 毫秒,将发送请求,但如果在该延迟之前发生另一次击键,您甚至不会发出请求。
function delayedEvent(eventHandler, delay) {
var lastTimeout = null;
return function () {
var that = this,
args= Array.prototype.slice.call(arguments).sort();
if (lastTimeout !== null)
window.clearTimeout(lastTimeout);
lastTimeout = window.setTimeout(function () {
eventHandler.apply(that, args);
}, delay);
};
}
$('... selector ...').on('event', delayedEvent(function () { ... your code ... }, 500));
编辑:这就是实现队列的方法,我还没有测试过这段代码,请以它为起点。
function eventQueue(requestMaker) {
// Here we store the last event queued, it'll wait until all the already running events are done.
var lastQueued = null,
runningQueue = [];
// Push a new event into the running queue
function pushRunning(toPush) {
runningQueue.push(toPush.done(
// It is necesary to use an IIFE to get the index by value and not by reference
(function (index) {
return function() {
// Remove this event from the runningqueue
runningQueue.splice(index, 1);
// If the queue has been completed, start running the last event sent
if (lastQueued !== null && runningQueue.length === 0) {
pushRunning(lastQueued());
}
}
}(runningQueue.lenght));
));
}
return function () {
var that = this,
args = Array.prototype.slice.call(arguments).sort();
// Some events are already running, just override the lastQueued
if (runningQueue.length > 0) {
lastQueued = requestMaker.bind(that, args);
} else {
// Nothing queued run this event straight away, and queue it as running
pushRunning(requestMaker.apply(that, args));
}
};
}
$('... selector ...').on('event', eventQueue(function () {
// It's extremely important that you return the jquery $.ajax Promise
return $.ajax(...);
}, 500));