all files / src/ ve.Scheduler.js

96% Statements 24/25
100% Branches 4/4
80% Functions 4/5
96% Lines 24/25
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110                                                                                                  10× 10× 10×                                                                          
/*!
 * VisualEditor Scheduler class.
 *
 * @copyright 2011-2019 VisualEditor Team and others; see http://ve.mit-license.org
 */
 
/**
 * @class
 *
 * @constructor
 */
ve.Scheduler = function VeScheduler() {
	// TODO: If we decide to start tracking setTimeout calls within actions, we'll
	// need to keep state here.
};
 
/* Inheritance */
 
OO.initClass( ve.Scheduler );
 
/* Static Properties */
 
ve.Scheduler.static.maxDelay = 1000;
 
/* Methods */
 
/**
 * Perform an action and await a callback when its side-effects are complete
 *
 * The ultimate definition of "side-effects are complete" is "when the chain of async
 * actions / setTimeout calls spawned by the action finish". This is intended to be a way
 * to wrap non-promise code in promises and have it mostly work.
 *
 * As currently implemented, we use completionTest as our sole signal. This is not
 * guaranteed to remain true. Don't write code that assumes completionTest will be
 * called, or which tests for a completely unrelated condition.
 *
 * The signature of this function is designed to let you leave signals about your intent.
 * You pass the action with side-effects in, and explain the conditions that must be met
 * for further actions to be taken.
 *
 * @param {Function} immediateAction Action to take whose status we want to track
 * @param {Function} completionTest Tests whether action is complete; ideally very cheap;
 *        there's no guarantee that we will ever call this, if we can sense completion in
 *        some other way
 * @param {number} [delayHint] Optional hint about how long to wait between tests
 * @return {jQuery.Promise} Promise that resolves when the completionTest returns true.
 *         Note that this _could_ already be resolved when it's returned, so there's no
 *         guarantee that your `done` call on it will be delayed.
 */
ve.Scheduler.prototype.schedule = function ( immediateAction, completionTest, delayHint ) {
	var deferred = ve.createDeferred(),
		startTime = this.now(),
		testThenAct = function () {
			var complete;
			try {
				complete = completionTest();
			} catch ( e ) {
				deferred.reject( e );
				return;
			}
			if ( complete ) {
				deferred.resolve();
				return;
			}
			if ( this.now() - startTime > this.constructor.static.maxDelay ) {
				deferred.reject();
				return;
			}
			this.postpone( testThenAct, delayHint );
		}.bind( this );
 
	// In the future, we may want to expand this to track whether other async calls
	// were made within the action.
	immediateAction();
 
	// Spin up the test cycle
	testThenAct();
 
	return deferred.promise();
};
 
/**
 * Make a postponed call.
 *
 * This is a separate function because that makes it easier to replace when testing
 *
 * @param {Function} callback The function to call
 * @param {number} delay Delay before running callback
 * @return {number} Unique postponed timeout id
 */
ve.Scheduler.prototype.postpone = function ( callback, delay ) {
	return setTimeout( callback, delay );
};
 
/**
 * Obtain the current timestamp
 *
 * This is a separate function because that makes it easier to replace when testing
 *
 * @return {number} Current timestamp in milliseconds
 */
ve.Scheduler.prototype.now = function () {
	return Date.now();
};
 
/* Initialization */
 
ve.scheduler = new ve.Scheduler();