Abort pending requests

Ensure all requests are abortable promises, and abort if
a new, conflicting request is made.

Change-Id: Ie05142f6da8cba6dde4f73c1b22960b726af4764
This commit is contained in:
Ed Sanders 2016-08-15 11:15:30 -07:00
parent f6a44f43fe
commit 6997f135db
4 changed files with 147 additions and 115 deletions

View file

@ -14,19 +14,27 @@
* Fetches a batch of revision data, including a gender setting for users who edited the revision
*
* @param {string} pageName
* @param {Object} options - Options containing callbacks for `success` and `error` as well as
* optional fields for: `dir (defaults to `older`), `limit` (defaults to 500), `startId`, `endId`,
* `knownUserGenders`
* @param {Object} options Options
* @param {string} [options.dir='older'] Sort direction
* @param {number} [options.limit=500] Result limit
* @param {number} [options.startId] Start ID
* @param {number} [options.endId] End ID
* @param {Object} [options.knownUserGenders] Known user genders
*/
fetchRevisionData: function ( pageName, options ) {
var self = this;
this.fetchRevisions( pageName, options )
var xhr, userXhr,
deferred = $.Deferred(),
self = this;
options = options || {};
xhr = this.fetchRevisions( pageName, options )
.done( function ( data ) {
var revs = data.query.pages[ 0 ].revisions,
/*jshint -W024 */
revContinue = data.continue,
/*jshint +W024 */
genderData = typeof options.knownUserGenders !== 'undefined' ? options.knownUserGenders : {},
genderData = options.knownUserGenders || {},
userNames;
if ( !revs ) {
@ -35,7 +43,7 @@
userNames = self.getUserNames( revs, genderData );
self.fetchUserGenderData( userNames )
userXhr = self.fetchUserGenderData( userNames )
.done( function ( data ) {
var users = typeof data !== 'undefined' ? data.query.users : [];
@ -49,34 +57,49 @@
}
} );
options.success( { revisions: revs, 'continue': revContinue } );
deferred.resolve( { revisions: revs, 'continue': revContinue } );
} )
.fail( options.error );
.fail( deferred.reject );
} )
.fail( options.error );
.fail( deferred.reject );
return deferred.promise( {
abort: function () {
xhr.abort();
if ( userXhr ) {
userXhr.abort();
}
}
} );
},
/**
* Fetches up to 500 revisions at a time
*
* @param {string} pageName
* @param {Object} options object containing optional options, fields: `dir` (defaults to `older`),
* `limit` (defaults to 500), `startId`, `endId`
* @return {jQuery}
* @param {Object} [options] Options
* @param {string} [options.dir='older'] Sort direction
* @param {number} [options.limit=500] Result limit
* @param {number} [options.startId] Start ID
* @param {number} [options.endId] End ID
* @return {jQuery.jqXHR}
*/
fetchRevisions: function ( pageName, options ) {
var dir = options.dir !== undefined ? options.dir : 'older',
data = {
action: 'query',
prop: 'revisions',
format: 'json',
rvprop: 'ids|timestamp|user|comment|parsedcomment|size|flags',
titles: pageName,
formatversion: 2,
'continue': '',
rvlimit: 500,
rvdir: dir
};
var dir, data;
options = options || {};
dir = options.dir !== undefined ? options.dir : 'older';
data = {
action: 'query',
prop: 'revisions',
format: 'json',
rvprop: 'ids|timestamp|user|comment|parsedcomment|size|flags',
titles: pageName,
formatversion: 2,
'continue': '',
rvlimit: 500,
rvdir: dir
};
if ( options.startId !== undefined ) {
data.rvstartid = options.startId;
@ -98,7 +121,7 @@
* Fetches gender data for up to 500 user names
*
* @param {string[]} users
* @return {jQuery}
* @return {jQuery.jqXHR}
*/
fetchUserGenderData: function ( users ) {
if ( users.length === 0 ) {
@ -118,7 +141,7 @@
},
/**
* @param {Array} revs
* @param {Object[]} revs
* @param {Object} knownUserGenders
* @return {string[]}
*/
@ -132,7 +155,7 @@
},
/**
* @param {Array} data
* @param {Object[]} data
* @return {Object}
*/
getUserGenderData: function ( data ) {

View file

@ -5,6 +5,7 @@
* @constructor
*/
var DiffPage = function () {
this.lastRequest = null;
};
$.extend( DiffPage.prototype, {
@ -13,45 +14,58 @@
*
* @param {number} revId1
* @param {number} revId2
* @param {number} [retryAttempt=0]
*/
refresh: function ( revId1, revId2 ) {
var data = {
diff: Math.max( revId1, revId2 ),
oldid: Math.min( revId1, revId2 )
},
refresh: function ( revId1, revId2, retryAttempt ) {
var self = this,
retryLimit = 2,
data = {
diff: Math.max( revId1, revId2 ),
oldid: Math.min( revId1, revId2 )
},
params = this.getExtraDiffPageParams();
retryAttempt = retryAttempt || 0;
if ( Object.keys( params ).length > 0 ) {
$.extend( data, params );
}
if ( this.lastRequest ) {
this.lastRequest.abort();
}
$( 'table.diff[data-mw="interface"]' )
.append( $( '<tr>' ) )
.append( $( '<td>' ) )
.append( $( '<div>' ).addClass( 'mw-revslider-darkness' ) );
$.ajax( {
this.lastRequest = $.ajax( {
url: mw.util.wikiScript( 'index' ),
data: data,
tryCount: 0,
retryLimit: 2,
success: function ( data ) {
var $container = $( '.mw-revslider-container' ),
$contentText = $( '#mw-content-text' ),
scrollLeft = $container.find( '.mw-revslider-revisions-container' ).scrollLeft();
tryCount: 0
} );
// Don't chain, so lastRequest is a jQuery.jqXHR object
this.lastRequest.then( function ( data ) {
var $data,
$container = $( '.mw-revslider-container' ),
$contentText = $( '#mw-content-text' ),
scrollLeft = $container.find( '.mw-revslider-revisions-container' ).scrollLeft();
data = $( data );
data.find( '.mw-revslider-container' )
.replaceWith( $container );
$contentText.html( data.find( '#mw-content-text' ) )
.find( '.mw-revslider-revisions-container' ).scrollLeft( scrollLeft );
$data = $( data );
$data.find( '.mw-revslider-container' ).replaceWith( $container );
$contentText.html( $data.find( '#mw-content-text' ) )
.find( '.mw-revslider-revisions-container' ).scrollLeft( scrollLeft );
mw.hook( 'wikipage.content' ).fire( $contentText );
},
error: function ( err ) {
mw.hook( 'wikipage.content' ).fire( $contentText );
}, function ( xhr ) {
$( 'table.diff[data-mw="interface"] .mw-revslider-darkness' ).remove();
if ( xhr.statusText !== 'abort' ) {
this.tryCount++;
console.log( err );
mw.track( 'counter.MediaWiki.RevisionSlider.error.refresh' );
if ( this.tryCount <= this.retryLimit ) {
if ( retryAttempt <= retryLimit ) {
console.log( 'Retrying request' );
$.ajax( this );
self.refresh( revId1, revId2, retryAttempt + 1 );
}
// TODO notify the user that we failed to update the diff?
// This could also attempt to reload the page with the correct diff loaded without ajax?

View file

@ -586,22 +586,21 @@
startId: revisions[ revisions.length - 1 ].getId(),
dir: 'newer',
limit: revisionCount + 1,
knownUserGenders: this.slider.getRevisions().getUserGenders(),
success: function ( data ) {
revs = data.revisions.slice( 1 );
if ( revs.length === 0 ) {
self.noMoreNewerRevisions = true;
return;
}
self.addRevisionsAtEnd( $slider, revs );
/*jshint -W024 */
if ( data.continue === undefined ) {
self.noMoreNewerRevisions = true;
}
/*jshint +W024 */
knownUserGenders: this.slider.getRevisions().getUserGenders()
} ).then( function ( data ) {
revs = data.revisions.slice( 1 );
if ( revs.length === 0 ) {
self.noMoreNewerRevisions = true;
return;
}
self.addRevisionsAtEnd( $slider, revs );
/*jshint -W024 */
if ( data.continue === undefined ) {
self.noMoreNewerRevisions = true;
}
/*jshint +W024 */
} );
},
@ -624,26 +623,25 @@
// fetch an extra revision if there are more older revision than the current "window",
// this makes it possible to correctly set a size of the bar related to the oldest revision to add
limit: revisionCount + 2,
knownUserGenders: this.slider.getRevisions().getUserGenders(),
success: function ( data ) {
revs = data.revisions.slice( 1 ).reverse();
if ( revs.length === 0 ) {
self.noMoreOlderRevisions = true;
return;
}
if ( revs.length === revisionCount + 1 ) {
precedingRevisionSize = revs[ 0 ].size;
revs = revs.slice( 1 );
}
self.addRevisionsAtStart( $slider, revs, precedingRevisionSize );
/*jshint -W024 */
if ( data.continue === undefined ) {
self.noMoreOlderRevisions = true;
}
/*jshint +W024 */
knownUserGenders: this.slider.getRevisions().getUserGenders()
} ).then( function ( data ) {
revs = data.revisions.slice( 1 ).reverse();
if ( revs.length === 0 ) {
self.noMoreOlderRevisions = true;
return;
}
if ( revs.length === revisionCount + 1 ) {
precedingRevisionSize = revs[ 0 ].size;
revs = revs.slice( 1 );
}
self.addRevisionsAtStart( $slider, revs, precedingRevisionSize );
/*jshint -W024 */
if ( data.continue === undefined ) {
self.noMoreOlderRevisions = true;
}
/*jshint +W024 */
} );
},

View file

@ -13,48 +13,45 @@
api.fetchRevisionData( mw.config.get( 'wgPageName' ), {
startId: mw.config.values.extRevisionSliderNewRev,
limit: mw.libs.revisionSlider.calculateRevisionsPerWindow( 160, 16 ),
limit: mw.libs.revisionSlider.calculateRevisionsPerWindow( 160, 16 )
} ).then( function ( data ) {
var revs,
revisionList,
$container,
slider;
success: function ( data ) {
var revs,
revisionList,
$container,
slider;
mw.track( 'timing.MediaWiki.RevisionSlider.timing.initFetchRevisionData', mw.now() - startTime );
mw.track( 'timing.MediaWiki.RevisionSlider.timing.initFetchRevisionData', mw.now() - startTime );
try {
revs = data.revisions;
revs.reverse();
try {
revs = data.revisions;
revs.reverse();
revisionList = new mw.libs.revisionSlider.RevisionList( mw.libs.revisionSlider.makeRevisions( revs ) );
revisionList = new mw.libs.revisionSlider.RevisionList( mw.libs.revisionSlider.makeRevisions( revs ) );
$container = $( '.mw-revslider-slider-wrapper' );
slider = new mw.libs.revisionSlider.Slider( revisionList );
slider.getView().render( $container );
$container = $( '.mw-revslider-slider-wrapper' );
slider = new mw.libs.revisionSlider.Slider( revisionList );
slider.getView().render( $container );
if ( !mw.user.options.get( 'userjs-revslider-hidehelp' ) ) {
mw.libs.revisionSlider.HelpDialog.show();
( new mw.Api() ).saveOption( 'userjs-revslider-hidehelp', true );
}
$( '.mw-revslider-placeholder' ).remove();
mw.track( 'timing.MediaWiki.RevisionSlider.timing.init', mw.now() - startTime );
} catch ( err ) {
$( '.mw-revslider-placeholder' )
.text( mw.message( 'revisionslider-loading-failed' ).text() );
console.log( err );
mw.track( 'counter.MediaWiki.RevisionSlider.error.init' );
if ( !mw.user.options.get( 'userjs-revslider-hidehelp' ) ) {
mw.libs.revisionSlider.HelpDialog.show();
( new mw.Api() ).saveOption( 'userjs-revslider-hidehelp', true );
}
initialized = true;
},
error: function ( err ) {
$( '.mw-revslider-placeholder' ).remove();
mw.track( 'timing.MediaWiki.RevisionSlider.timing.init', mw.now() - startTime );
} catch ( err ) {
$( '.mw-revslider-placeholder' )
.text( mw.message( 'revisionslider-loading-failed' ).text() );
console.log( err );
mw.track( 'counter.MediaWiki.RevisionSlider.error.init' );
}
initialized = true;
}, function ( err ) {
$( '.mw-revslider-placeholder' )
.text( mw.message( 'revisionslider-loading-failed' ).text() );
console.log( err );
mw.track( 'counter.MediaWiki.RevisionSlider.error.init' );
} );
};