Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
147 lines
3.5 KiB
JavaScript
Executable file
147 lines
3.5 KiB
JavaScript
Executable file
/**
|
|
* TaskScheduler helps manage tasks and ensures they are executed in
|
|
* sequential order. When a task of a certain type is enqueued, all pending
|
|
* tasks of the same type in the queue are removed. This avoids redundant
|
|
* queries and improves user perceived performance.
|
|
*
|
|
* @module modules/base/task_scheduler
|
|
*/
|
|
define(function(require) {
|
|
'use strict';
|
|
|
|
var Defer = require('modules/defer');
|
|
var Module = require('modules/base/module');
|
|
var Observable = require('modules/mvvm/observable');
|
|
|
|
/**
|
|
* @class TaskScheduler
|
|
* @returns {TaskScheduler}
|
|
*/
|
|
var TaskScheduler = Module.create(function TaskScheduler() {
|
|
this.super(Observable).call(this);
|
|
this.isLocked = false;
|
|
this._tasks = [];
|
|
}).extend(Observable);
|
|
|
|
Observable.defineObservableProperty(TaskScheduler.prototype, 'isLocked', {
|
|
value: false
|
|
});
|
|
|
|
const TASK_TYPE = {
|
|
GENERAL: 0
|
|
};
|
|
|
|
/**
|
|
* We will keep some constants here as a static variable
|
|
*
|
|
* @access public
|
|
* @memberOf TaskScheduler
|
|
*/
|
|
Object.defineProperty(TaskScheduler, 'TASK_TYPE', {
|
|
enumerable: true,
|
|
configurable: false,
|
|
get: function() {
|
|
return TASK_TYPE;
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Change the internal state to make sure we are locked.
|
|
*
|
|
* @access private
|
|
* @memberOf TaskScheduler
|
|
*/
|
|
TaskScheduler.prototype._lock = function() {
|
|
this.isLocked = true;
|
|
};
|
|
|
|
/**
|
|
* Change the internal state to unlocked and execute next task.
|
|
*
|
|
* @access private
|
|
* @memberOf TaskScheduler
|
|
*/
|
|
TaskScheduler.prototype._unlock = function() {
|
|
this.isLocked = false;
|
|
this._executeNextTask();
|
|
};
|
|
|
|
/**
|
|
* We will remove any redundant same-type tasks and keep the remaining ones
|
|
* including the others which are not cancelable (general use task).
|
|
*
|
|
* @access private
|
|
* @memberOf TaskScheduler
|
|
* @param {String} type
|
|
* @return {Array} array of tasks
|
|
*/
|
|
TaskScheduler.prototype._removeRedundantTasks = function(type) {
|
|
return this._tasks.filter((task) => {
|
|
if (!task.cancelable) {
|
|
return true;
|
|
} else {
|
|
return task.type !== type;
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Execute the next task when unlocked.
|
|
*
|
|
* @access private
|
|
* @returns {Promise}
|
|
* @memberOf TaskScheduler
|
|
*/
|
|
TaskScheduler.prototype._executeNextTask = function() {
|
|
if (this.isLocked) {
|
|
return;
|
|
}
|
|
var nextTask = this._tasks.shift();
|
|
if (nextTask) {
|
|
this._lock();
|
|
return nextTask.func().then((result) => {
|
|
nextTask.defer.resolve(result);
|
|
this._unlock();
|
|
}, () => {
|
|
nextTask.defer.reject();
|
|
this._unlock();
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This is the only entry point for caller, and we will enqueue all tasks
|
|
* one by one and execute them in order.
|
|
*
|
|
* @access public
|
|
* @memberOf TaskScheduler
|
|
* @param {Object} task
|
|
* @param {String} task.type
|
|
* @param {Function} task.func - make sure the func would return a promise
|
|
* @param {Boolean} task.cancelable
|
|
* @return {Promise}
|
|
*/
|
|
TaskScheduler.prototype.enqueue = function(task) {
|
|
var defer = Defer();
|
|
|
|
// if there is no func, let's directly resolve this task
|
|
if (!task.func) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
if (!task.type) {
|
|
// copy the task and make sure we won't change the previous one
|
|
task = Object.assign({}, task);
|
|
task.type = TaskScheduler.TASK_TYPE.GENERAL;
|
|
}
|
|
|
|
task.defer = defer;
|
|
this._tasks = this._removeRedundantTasks(task.type);
|
|
this._tasks.push(task);
|
|
this._executeNextTask();
|
|
|
|
return defer.promise;
|
|
};
|
|
|
|
return TaskScheduler;
|
|
});
|