2015-10-12 9 views
5

Sto facendo una webapp con html + jquery e un backend di servizio di restituzione java. Ho un campo di testo, con suggerimenti di tipo typeahead, quindi ogni carattere che l'utente digita nel campo attiverà un giro di andata e ritorno del server e aggiornerà l'elenco di suggerimenti typeahead.jquery ajax: si verificano eventi di tipo typeahead nell'ordine corretto?

parti essenziali del codice:

var showTypeaheadSuggestions = function(data) { 
     // update ui-element ... 
    } 

    var displayFailure = function() { 
     // update ui-element ... 
    } 

    var searchText = $("#searchText"); 
    var searchTextKeyup = function() { 
     var txt = searchText.val(); 
     $.ajax({ 
      url : typeaheadUrl(txt), 
      type : 'GET', 
      dataType : 'json', 
     }).done(showTypeaheadSuggestions).fail(displayFailure); 
    } 
    searchText.on('keyup', searchTextKeyup); 

Si tratta fondamentalmente di lavoro. Ma stavo pensando a cosa succede se si digita, ad esempio, 2 lettere "ab" (che innescherà prima una richiesta di "a" e quindi una richiesta di "ab") ...

Quindi, cosa succede se la risposta "a" richiede un po 'più tempo per elaborare e arriva dopo la risposta "ab"? Ho bisogno di rilevare nel mio codice, per eliminare la risposta "a"?

In http://api.jquery.com/jquery.ajax/ lo fa dice:

Promessa callback - .done(), .fail(), .sempre(), e .then() - sono invocato, nell'ordine in cui sono sono registrati

Che cosa significa esattamente? Speravo che questo significasse che $ .ajax() gestiva automaticamente lo scenario sopra corretto.

Ma quando faccio un piccolo test (sul lato server ho semplicemente iniettato un 2 secondi sonno-ritardo, solo quando la ricerca stringa è esattamente "a"), si scopre lo fa non si comportano come mi aspettavo.

L'elenco di typeahead viene innanzitutto aggiornato con la risposta "ab" e quindi quando arriva la risposta "a" , viene aggiornata anche la lista dei tipi di caratteri che ricevono suggerimenti errati.

Qual è il modo stabilito di gestirlo correttamente?

risposta

1

C'è un altro approccio se si vuole mantenere il codice lato server senza modifiche. Si può effettivamente avvolgere le funzioni di ritorno all'interno di una classe e di creare istanze per ogni richiesta, quindi memorizzare l'ultima istanza in una variabile ambito globale e verificare se il proprietario del metodo invocato non corrisponde l'ultima istanza:

var lastRequest; 
var searchText = $("#searchText"); 

function requestClass() 
{ 
    var that = this; 

    this.showTypeaheadSuggestions = function(data) { 
     //Update ui-element 
     if (lastRequest == that) 
      console.log('suggestions retrieved: ' + data); 
     else 
      console.log('this response (' + data + ') is ignored'); 
    }; 

    this.displayFailure = function() { 
     //Update ui-element 
     console.log('failure'); 
    }; 
} 

var searchTextKeyup = function() { 
    var request = new requestClass(); 
    lastRequest = request; 

    var txt = searchText.val(); 
    $.ajax({ 
     url : typeaheadUrl(txt), 
     type : 'GET', 
     dataType : 'json', 
    }).done(request.showTypeaheadSuggestions).fail(request.displayFailure); 
} 

searchText.on('keyup', searchTextKeyup); 

I hanno testato questo con il piccolo-test avete proposto per la questione (l'aggiunta di un ritardo di 2 secondi quando la stringa di ricerca non corrisponde al 'un' carattere) ed il risultato è il seguente:

suggestions retrieved: ab 
this response (a) is ignored 
+0

Interessante ... anche se mi sto sforzando di capire come funziona davvero :) --- Cosa valuta l'espressione "nuova richiestaClass()" in realtà? ---- requestClass() è una chiamata alla funzione senza ritorno stmt, quindi dovresti valutare (se ricordo bene) l'ultima espressione nella funzione ... quale qui è cosa? :) – Rop

+0

La parola chiave 'new' crea un'istanza di un oggetto, imposta la sua funzione di costruzione su' requestClass() 'e la chiama. Poiché la funzione non restituisce alcun risultato, il 'new requestClass()' restituirà il riferimento all'oggetto istanziato. Dai un'occhiata [qui] (http://stackoverflow.com/questions/1646698/what-is-the-new-keyword-in-javascript#answer-3658673) per saperne di più sulla parola chiave 'new'. –

1

Uno dei metodi con cui ho affrontato questo problema era assegnare un ID per ogni volta che lo si chiama e passarlo come ID sul lato server. Quando il tuo server ha finito di elaborarlo, restituisce il risultato insieme al suo id.

Quindi, ogni volta che viene eseguito il codice lato client, l'ID aumenterà. Ad esempio:

var callback_id = 0; 

var searchText = $("#searchText"); 
var searchTextKeyup = function() { 
    callback_id ++; 
    var txt = searchText.val(); 
    $.ajax({ 
     url : typeaheadUrl(txt), 
     data : callback_id, 
     type : 'GET', 
     dataType : 'json', 
    }).done(showTypeaheadSuggestions).fail(displayFailure); 
} 
searchText.on('keyup', searchTextKeyup); 

Quindi, quando si riceve la risposta, si controlla se l'ID è quello corrente. Nel caso in cui l'utente lanci due eventi contemporaneamente, il tuo evento ajax verrà attivato due volte, una volta con callback_id = 0 e una con callback_id = 1.

L'ultima cosa che devi fare è un'istruzione if che aggiorna solo il tuo TypeaheadSuggestions se lo callback_id è il più recente confrontando l'ID inviato di nuovo dalla risposta del tuo server.

1

È necessario confrontare il nuovo testo di input con il testo inviato e, se questo è ciò che l'utente desidera trovare, lo mostrerà, altrimenti non fare nulla con la risposta.

Ad esempio:

var searchText = $("input").val() 
$.ajax({ 
     .... 
     data: {searchText : searchText} 
     success: funtion(){ 
     if($("input").val()==searchText){ 
      //SHOW RESULTS 
     } 
    } 
}) 
+0

Questo metodo è migliore dell'ID tempo, perché se si scrive "a", che "ab", di "a" e se si ottiene risposta su "a" lo mostrerà (e si effettuano 2 chiamate anziché 3 per tempo id). –

+0

Ho bisogno di pensare un po ', forse ho torto) –

1

l'interfaccia promesse ritorna un "oggetto promessa" immediatamente in modo da poter utilizzare una sintassi diversa per il callback.

Invece di:

asyncCall(callback); 

È possibile utilizzare:

asyncCall() 
    .then(callback); 

E è possibile concatenare questi:

authorizeAccount() 
    .then(getNames) 
    .then(processNames); 

saranno eseguiti i callback nell'ordine in cui li registra - - processNames attenderà che getNames risolva prima.

Il modo "stabilito" per gestire questo "correttamente" è probabilmente quello di aggiungere qualche debouncing sul lato client in modo che solo l'intera richiesta ('query' invece di 'q', 'qu' 'que' ...) viene elaborato quando si digita la parola: http://underscorejs.org/#debounce con un timeout che scade una risposta se è necessario troppo tempo per tornare indietro.