requestQueue.js


goog.provide("spv.RequestQueue");

/**
 * This class keeps track of requests that need to be executed in order.
 * The requests are often not idempotent and should be in a defined order to
 * avoid surprising behaviors. DsClient and its derivatives usually prefixes
 * state bound calls with 'session'; these methods should as a rule always use
 * this request queue as a guard.
 * @export
 * @constructor
*/
spv.RequestQueue = function() {
	/**
	 * @private
	 * @type {Array.<Function>}
	 */
	this._request_queue = [];
	/**
	 * @private
	 * @type {Function}
	 */
	this._request_in_progress = null;
}


/**
 * Enqueue a request to be executed in a synchronized fasion.
 *
 * @param {function(function(Function))} request
 */
spv.RequestQueue.prototype.push = function(request) {
	this._request_queue.push(request);
	if (this._request_in_progress) {
		return;
	}
	this.next(null);
}


/**
 * Session requests that require synchronization must use this function.
 *
 * @param {?Function=} currentRequest
 */
spv.RequestQueue.prototype.next = function(currentRequest) {
	var queue = this;
	if (this._request_in_progress
	&&  currentRequest != this._request_in_progress) {
		throw new Error("Dequeue out of order.");
	}
	var next_request = this._request_queue.shift();
	if (!next_request) {
		this._request_in_progress = null;
		return;
	}
	this._request_in_progress = next_request;
	var execute = function() {
		next_request(wrap);
	}
	setTimeout(execute , 0);

	/**
	 * @param {?Function} callback
	 */
	function wrap(callback) {
		return function() {
			if(next_request.abort) return;
			try {
				callback.apply(null, arguments);
				queue.next(next_request);
			}
			catch(err) {
				queue.clear();
				throw err;
			}
		}
	};
};


/**
 * Clears the queue and aborts in flight requests so that their callbacks
 * are not triggered.
 */
spv.RequestQueue.prototype.clear = function() {
	if(this._request_in_progress) {
		this._request_in_progress.abort = true;
	}
	this._request_in_progress = null;
	for(var i=0; i<this._request_queue; ++i) {
		this._request_queue[i].abort = true;
	}
	this._request_queue.length = 0;
}