import {defer, requestAnimationFrame} from "./core"; /** * Queue for handling tasks one at a time * @class * @param {scope} context what this will resolve to in the tasks */ class Queue { constructor(context){ this._q = []; this.context = context; this.tick = requestAnimationFrame; this.running = false; this.paused = false; } /** * Add an item to the queue * @return {Promise} */ enqueue() { var deferred, promise; var queued; var task = [].shift.call(arguments); var args = arguments; // Handle single args without context // if(args && !Array.isArray(args)) { // args = [args]; // } if(!task) { throw new Error("No Task Provided"); } if(typeof task === "function"){ deferred = new defer(); promise = deferred.promise; queued = { "task" : task, "args" : args, //"context" : context, "deferred" : deferred, "promise" : promise }; } else { // Task is a promise queued = { "promise" : task }; } this._q.push(queued); // Wait to start queue flush if (this.paused == false && !this.running) { // setTimeout(this.flush.bind(this), 0); // this.tick.call(window, this.run.bind(this)); this.run(); } return queued.promise; } /** * Run one item * @return {Promise} */ dequeue(){ var inwait, task, result; if(this._q.length && !this.paused) { inwait = this._q.shift(); task = inwait.task; if(task){ // console.log(task) result = task.apply(this.context, inwait.args); if(result && typeof result["then"] === "function") { // Task is a function that returns a promise return result.then(function(){ inwait.deferred.resolve.apply(this.context, arguments); }.bind(this), function() { inwait.deferred.reject.apply(this.context, arguments); }.bind(this)); } else { // Task resolves immediately inwait.deferred.resolve.apply(this.context, result); return inwait.promise; } } else if(inwait.promise) { // Task is a promise return inwait.promise; } } else { inwait = new defer(); inwait.deferred.resolve(); return inwait.promise; } } // Run All Immediately dump(){ while(this._q.length) { this.dequeue(); } } /** * Run all tasks sequentially, at convince * @return {Promise} */ run(){ if(!this.running){ this.running = true; this.defered = new defer(); } this.tick.call(window, () => { if(this._q.length) { this.dequeue() .then(function(){ this.run(); }.bind(this)); } else { this.defered.resolve(); this.running = undefined; } }); // Unpause if(this.paused == true) { this.paused = false; } return this.defered.promise; } /** * Flush all, as quickly as possible * @return {Promise} */ flush(){ if(this.running){ return this.running; } if(this._q.length) { this.running = this.dequeue() .then(function(){ this.running = undefined; return this.flush(); }.bind(this)); return this.running; } } /** * Clear all items in wait */ clear(){ this._q = []; } /** * Get the number of tasks in the queue * @return {number} tasks */ length(){ return this._q.length; } /** * Pause a running queue */ pause(){ this.paused = true; } /** * End the queue */ stop(){ this._q = []; this.running = false; this.paused = true; } } /** * Create a new task from a callback * @class * @private * @param {function} task * @param {array} args * @param {scope} context * @return {function} task */ class Task { constructor(task, args, context){ return function(){ var toApply = arguments || []; return new Promise( (resolve, reject) => { var callback = function(value, err){ if (!value && err) { reject(err); } else { resolve(value); } }; // Add the callback to the arguments list toApply.push(callback); // Apply all arguments to the functions task.apply(context || this, toApply); }); }; } } export default Queue; export { Task };