lib/TickEntry.js
import {addToSemiInfiniteLoop} from './scheduler';
import Notifier from './Notifier';
import {ErrorMsg} from './contants';
/**
* @private
* @param {Object} tickEntry
* @return {void}
*/
function _checkError(tickEntry){
if(!tickEntry){
throw new Error('Ticker: instance can\'t be null');
} else if(!(tickEntry instanceof TickEntry)){
const className = tickEntry.constructor ? tickEntry.constructor.name : typeof tickEntry;
throw new Error(`Ticker: Expecting instance of TickEntry got ${className}`);
} else if(!tickEntry.func){
throw new Error('Ticker: function can\'t be undefined');
}
}
/**
* Tick Entry is a wrapper to the function we want to execute later in request animation frame or event cycle
* @example
* import Ticker from 'ticker'
* let tickEntry = new TickEntry(context,listener,callback,priority);
*/
export default class TickEntry
{
/**
* @param {Function} func
* @param {Object} context The "this" argument for the func.
* @param {number} priority
*/
constructor(func, context = null, priority = 0)
{
/**
* @type {Object}
*/
this.context = context;
/**
* @type {Function}
*/
this.func = func;
/**
* @type {number}
*/
this.priority = priority;
/**
* @type {number}
*/
this.executionCount = 0;
this.notifier = new Notifier();
_checkError(this);
}
onDone(doneCallback){
this.notifier.doneCallback = doneCallback;
return this.notifier;
}
onError(errorCallback){
this.notifier.errorCallback = errorCallback;
return this.notifier;
}
/**
* This function dispose the tickEntry references for garbage collection
*
* @return {void}
*/
dispose(){
this.context = null;
this.func = null;
this.priority = null;
this.executionCount = NaN;
this.notifier = null;
}
/**
* This function adds the func to event cycle / request animation frame for execution
*
* @return {void}
*/
executeInCycle(){
_checkError(this);
const tickEntryInstance = this;
const {func, context, priority} = tickEntryInstance;
function loopFunction(){
const notifier = tickEntryInstance.notifier;
const {doneCallback, errorCallback} = notifier;
try{
const result = func.call(context);
tickEntryInstance.executionCount++;
doneCallback && doneCallback(result);
} catch (error){
if(errorCallback){
errorCallback(error);
} else {
console.log(error)
}
tickEntryInstance.dispose();
}
}
addToSemiInfiniteLoop(loopFunction,priority);
this.notifier.onProgress = undefined;
return this.notifier;
}
/**
* This function adds the loop function to event cycle / request animation frame for execution
*
* @param {number} maxLoopPerFrame indicates loop length that needs to be executed in single frame or tick.
* @param {number} endIndex indicates loop end value
* @param {number} startIndex indicates loop begining value , default value is 0
* @return {void}
*/
executeAsSmallLoopsInCycle(maxLoopPerFrame, endIndex, startIndex = 0){
if(maxLoopPerFrame === undefined || typeof maxLoopPerFrame !== 'number'){
throw new Error(ErrorMsg.MAX_LOOP_PER_FRAME);
}
if(endIndex === undefined || typeof endIndex !== 'number'){
throw new Error(ErrorMsg.END_INDEX);
}
if(typeof startIndex !== 'number'){
throw new Error(ErrorMsg.START_INDEX);
}
_checkError(this);
const tickEntryInstance = this;
const {func, context, priority} = tickEntryInstance;
let loopLimit = maxLoopPerFrame;
let i = startIndex;
function loopFunction(){
const notifier = tickEntryInstance.notifier;
const {doneCallback, errorCallback, progressCallback} = notifier;
let result;
for(;i < loopLimit; i++){
try {
result = func.call(context, i);
} catch(error) {
errorCallback && errorCallback(error);
tickEntryInstance.dispose();
return;
}
}
if(loopLimit < endIndex){
loopLimit = loopLimit + maxLoopPerFrame;
progressCallback && progressCallback(i, result);
addToSemiInfiniteLoop(loopFunction,priority);
} else if( i === endIndex){
tickEntryInstance.executionCount++;
doneCallback && doneCallback(result);
}
}
addToSemiInfiniteLoop(loopFunction, priority);
return this.notifier;
}
}
TickEntry.HIGH = 0;
TickEntry.NORMAL = 1;
TickEntry.LOW = 2;
TickEntry.allowedTickCount = 100;