can-queues
Version:
A light weight JavaScript task queue
156 lines (129 loc) • 3.65 kB
JavaScript
"use strict";
var Queue = require( "./queue" );
var sortedIndexBy = require("./sorted-index-by");
var elementSort = require("./element-sort");
var canSymbol = require("can-symbol");
var canElementSymbol = canSymbol.for("can.element");
// TODO: call sortable queue and take how it should be sorted ...
function sortTasks(taskA, taskB){
// taskA - in the document?
// taskA - given a number?
//
return elementSort.sortOrder(taskA.meta.element, taskB.meta.element);
}
var DomOrderQueue = function () {
Queue.apply( this, arguments );
// A map of a task's function to the task for that function.
// This is so we can prevent duplicate functions from being enqueued
// and so `flushQueuedTask` can find the task and run it.
this.taskMap = new Map();
this.unsortable = [];
this.isFlushing = false;
};
DomOrderQueue.prototype = Object.create( Queue.prototype );
DomOrderQueue.prototype.constructor = DomOrderQueue;
DomOrderQueue.prototype.enqueue = function ( fn, context, args, meta ) {
var task;
// Only allow the enqueing of a given function once.
if ( !this.taskMap.has( fn ) ) {
if(!meta) {
meta = {};
}
if(!meta.element) {
meta.element = fn[canElementSymbol];
}
task = {
fn: fn,
context: context,
args: args,
meta: meta
};
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
if( !meta.element ) {
throw new Error("DomOrderQueue tasks must be created with a meta.element.");
}
}
//!steal-remove-end
this.taskMap.set( fn, task );
var index = sortedIndexBy(sortTasks, this.tasks, task);
this.tasks.splice(index, 0, task);
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
this._logEnqueue( task );
}
//!steal-remove-end
if ( this.tasks.length === 1 ) {
this.callbacks.onFirstTask( this );
}
} else {
// update the task with the new data
// TODO: ideally this would key off the mutation instead of the function.
// We could make it key off the element and function, not just function.
task = this.taskMap.get( fn );
task.context = context;
task.args = args;
if(!meta) {
meta = {};
}
if(!meta.element) {
meta.element = fn[canElementSymbol];
}
task.meta = meta;
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
this._logEnqueue( task );
}
//!steal-remove-end
}
};
DomOrderQueue.prototype.flush = function () {
// Only allow one task to run at a time.
if ( this.isFlushing ) {
return;
}
this.isFlushing = true;
while ( this.tasks.length ) {
var task = this.tasks.shift();
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
this._logFlush( task );
}
//!steal-remove-end
this.taskMap["delete"]( task.fn );
task.fn.apply( task.context, task.args );
}
this.isFlushing = false;
this.callbacks.onComplete( this );
};
DomOrderQueue.prototype.isEnqueued = function ( fn ) {
return this.taskMap.has( fn );
};
DomOrderQueue.prototype.flushQueuedTask = function ( fn ) {
var task = this.dequeue(fn);
if(task) {
//!steal-remove-start
if(process.env.NODE_ENV !== 'production') {
this._logFlush( task );
}
//!steal-remove-end
task.fn.apply( task.context, task.args );
}
};
DomOrderQueue.prototype.dequeue = function(fn){
var task = this.taskMap.get( fn );
if ( task ) {
var index = this.tasks.indexOf(task);
if ( index >= 0 ) {
this.tasks.splice( index, 1 );
this.taskMap["delete"]( task.fn );
return task;
} else {
console.warn("Task", fn, "has already run");
}
}
};
DomOrderQueue.prototype.tasksRemainingCount = function () {
return this.tasks.length;
};
module.exports = DomOrderQueue;