Skip to content

Commit 96f9c84

Browse files
committed
Autocomplete: Move race condition logic from ajax requests to general response handler. Fixes #8234 - Autocomplete: Automatic race-condition handling for custom sources.
1 parent 4ade134 commit 96f9c84

File tree

2 files changed

+49
-18
lines changed

2 files changed

+49
-18
lines changed

tests/unit/autocomplete/autocomplete_core.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,4 +123,33 @@ test( "allow form submit on enter when menu is not active", function() {
123123
}
124124
})();
125125

126+
asyncTest( "handle race condition", function() {
127+
expect( 3 );
128+
var count = 0,
129+
element = $( "#autocomplete" ).autocomplete({
130+
source: function( request, response ) {
131+
count++;
132+
if ( request.term.length === 1 ) {
133+
equal( count, 1, "request with 1 character is first" );
134+
setTimeout(function() {
135+
response([ "one" ]);
136+
setTimeout( checkResults, 1 );
137+
}, 1 );
138+
return;
139+
}
140+
equal( count, 2, "request with 2 characters is second" );
141+
response([ "two" ]);
142+
}
143+
});
144+
145+
element.autocomplete( "search", "a" );
146+
element.autocomplete( "search", "ab" );
147+
148+
function checkResults() {
149+
equal( element.autocomplete( "widget" ).find( ".ui-menu-item" ).text(), "two",
150+
"correct results displayed" );
151+
start();
152+
}
153+
});
154+
126155
}( jQuery ) );

ui/jquery.ui.autocomplete.js

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -186,9 +186,6 @@ $.widget( "ui.autocomplete", {
186186
self._change( event );
187187
});
188188
this._initSource();
189-
this.response = function() {
190-
return self._response.apply( self, arguments );
191-
};
192189
this.menu = $( "<ul></ul>" )
193190
.addClass( "ui-autocomplete" )
194191
.appendTo( this.document.find( this.options.appendTo || "body" )[0] )
@@ -326,18 +323,11 @@ $.widget( "ui.autocomplete", {
326323
url: url,
327324
data: request,
328325
dataType: "json",
329-
context: {
330-
autocompleteRequest: ++requestIndex
331-
},
332326
success: function( data, status ) {
333-
if ( this.autocompleteRequest === requestIndex ) {
334-
response( data );
335-
}
327+
response( data );
336328
},
337329
error: function() {
338-
if ( this.autocompleteRequest === requestIndex ) {
339-
response( [] );
340-
}
330+
response( [] );
341331
}
342332
});
343333
};
@@ -380,10 +370,26 @@ $.widget( "ui.autocomplete", {
380370
this.element.addClass( "ui-autocomplete-loading" );
381371
this.cancelSearch = false;
382372

383-
this.source( { term: value }, this.response );
373+
this.source( { term: value }, this._response() );
374+
},
375+
376+
_response: function() {
377+
var that = this,
378+
index = ++requestIndex;
379+
380+
return function( content ) {
381+
if ( index === requestIndex ) {
382+
that.__response( content );
383+
}
384+
385+
that.pending--;
386+
if ( !that.pending ) {
387+
that.element.removeClass( "ui-autocomplete-loading" );
388+
}
389+
};
384390
},
385391

386-
_response: function( content ) {
392+
__response: function( content ) {
387393
if ( content ) {
388394
content = this._normalize( content );
389395
}
@@ -395,10 +401,6 @@ $.widget( "ui.autocomplete", {
395401
// use ._close() instead of .close() so we don't cancel future searches
396402
this._close();
397403
}
398-
this.pending--;
399-
if ( !this.pending ) {
400-
this.element.removeClass( "ui-autocomplete-loading" );
401-
}
402404
},
403405

404406
close: function( event ) {

0 commit comments

Comments
 (0)