Estoy realizando una búsqueda en vivo del servidor mientras un usuario está escribiendo en el navegador. Mi funcionalidad de búsqueda no puede seguir el ritmo de escritura, por lo que las solicitudes se acumulan.

Quiero poder cancelar consultas que se reemplazan por nuevas consultas antes de que el servidor esté listo para responder.

Esto es lo que quiero lograr:

  • La primera pulsación de tecla debe enviarse al servidor.
  • La secuencia de comandos del lado del cliente debe poner en cola el siguiente golpe de tecla hasta que el servidor responda a la primera consulta.
  • Si, mientras tanto, ocurre otra pulsación de tecla, debería reemplazar la pulsación de tecla actualmente en cola.

De esta manera, solo la pulsación de tecla más reciente se envía al servidor, una vez que el servidor está listo. Por supuesto, si no hay cola, solo se debe enviar una pulsación de tecla directamente al servidor.

Mi implementación actual es muy simple:

$.ajax({
  type: "get",
  dataType: "script",
  data: {
    search: "something"
  },
  url: "/api/search",
  success: function(data, status, xhr) {},
  error: function(xhr, textStatus, errorThrown) {}
});
0
ChristofferJoergensen 9 may. 2016 a las 16:02

3 respuestas

La mejor respuesta

Creo que su enfoque va a inundar el servidor, porque está enviando una solicitud al servidor en cada pulsación de tecla. E incluso si cancela la solicitud en el lado del cliente como dijo @SravanS, el servidor seguirá recibiendo y comenzará a procesarlos. La solicitud HTTP una vez enviada ya está en la red, no puede detenerla, lo que puede hacer es que el cliente no la cancele, lo que supongo que notifica al servidor o simplemente ignora la respuesta que envía, pero aún así está inundando eso.

Quizás implementar un retraso para reconocer cuándo el usuario dejó de escribir sería el mejor enfoque. Esta es una fábrica genérica de controladores de eventos diferidos, realmente fácil de usar, simplemente transfiérale su función y asígnele un retraso. Si X milisegundos pasaron entre las pulsaciones de teclas, se enviará una solicitud, pero si ocurre otra pulsación de tecla antes de ese retraso, ni siquiera va a hacer una solicitud.

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));

EDITAR : así es como puede implementar la cola, no he probado este código, úselo como punto de partida.

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));
2
Marco Scabbiolo 9 may. 2016 a las 14:36
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)
}

Pero en sendAjax haz algo así:

.onSuccess = function () {flag = true;}
.onError = function () {flag = true;}

Es bastante primitivo, pero puedes desarrollarlo, es más elástico que funcional en esta etapa.

"Pero funciona, y las cosas que funcionan no son estúpidas" :)

0
Daniel Mizerski 9 may. 2016 a las 13:19
var req = $.ajax();

//kill the request
req.abort()

Puede usar el método $ .ajax.abort para eliminar la solicitud. En cada pulsación de tecla, elimine la solicitud anterior, si hubiera una.

var prevReq = {};
elem.onkeyup = function(){
    if(prevReq.abort)
        prevReq.abort();    
    var prevReq = $.ajax(/*make the api call and clear prevReq object on response*/);    
};
0
Sravan S 9 may. 2016 a las 13:10