服务器仍在处理上一个请求时如何排队 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));