// async.js (C) Walter Bislin // url: http://walter.bislins.ch/doku/async // // Usage: // // MyClass.prototype.myLoopFunction = function( aParam ) { // do one loop step ... // aParam.Ix++; // return aParam.Ix < last; // } // // MyClass.prototype.myLastFunction = function( ) { // do last thing... // } // // MyClass.prototype.myAsyncFunctions = function( ) { // Async.Call( this, this.myLoopFunction, { Ix: 0 } ); // Async.Call( this, this.myLastFunction ); // } // // var myObj = new MyClass(); // myObj.myAsyncFunctions(); function CAsync() { this.RunTime = 12; this.WaitTime = 0; this.TimerStartTime = 0; this.SyncMode = false; this.Debug = false; // true -> GetElapsedTime returns always 0 this.OnException = function(err,caller) { alert(err); return true; }; this.WaitingQueue = { Head: null, Tail: null }; this.RunningQueue = { Head: null, Tail: null }; this.Timer = null; } // Private Functions CAsync.prototype.ClearQueue = function ( aQueue ) { aQueue.Tail = null; if (!aQueue.Head) return; while (aQueue.Head) { var qe = aQueue.Head; aQueue.Head = qe.Next; qe.Next = null; } } CAsync.prototype.AppendQueueElement = function ( aQueue, aQueueElement ) { if (aQueue.Tail) aQueue.Tail.Next = aQueueElement; aQueue.Tail = aQueueElement; if (!aQueue.Head) aQueue.Head = aQueueElement; } CAsync.prototype.AppendQueue = function ( aQueue1, aQueue2 ) { if (!aQueue2.Head) return; if (aQueue1.Tail) aQueue1.Tail.Next = aQueue2.Head; aQueue1.Tail = aQueue2.Tail; if (!aQueue1.Head) aQueue1.Head = aQueue2.Head; aQueue2.Head = null; aQueue2.Tail = null; } CAsync.prototype.RemoveFirstQueueElement = function ( aQueue ) { if (!aQueue.Head) return null; var qe = aQueue.Head; aQueue.Head = qe.Next; qe.Next = null; if (!aQueue.Head) aQueue.Tail = null; return qe; } CAsync.prototype.NewQueueElement = function ( aObj, aFn, aParam, aDelay, aBeforeCallCB, aAfterCallCB ) { return { Next: null, Obj: aObj, Fn: aFn, Param: aParam, Delay: aDelay, BeforeCallCB: aBeforeCallCB, AfterCallCB: aAfterCallCB }; } CAsync.prototype.BeginQueue = function () { this.RunningQueue.Head = this.WaitingQueue.Head; this.RunningQueue.Tail = this.WaitingQueue.Tail; this.WaitingQueue.Head = null; this.WaitingQueue.Tail = null; } CAsync.prototype.EndQueue = function ( bKeepCurrentElement ) { if (!bKeepCurrentElement) { this.RemoveFirstQueueElement( this.RunningQueue ); } this.AppendQueue( this.WaitingQueue, this.RunningQueue ); } CAsync.prototype.StartAsyncCalls = function () { var again = true; while (again) { if (this.RunningQueue.Head) return; if (!this.SyncMode && this.Timer) return; var qe = this.WaitingQueue.Head; if (!qe) return; var me = this; if (qe.BeforeCallCB) { try { this.CallFn( qe.BeforeCallCB, qe ); } catch(err) { } } if (this.SyncMode) { again = this.OnTimer(); } else { var time = this.WaitTime; if (qe.Delay > 0) time = qe.Delay; this.Timer = setTimeout( function(){ me.OnTimer(); }, time ); return; } } } CAsync.prototype.CallFn = function ( aFn, aQueueElement ) { if (aQueueElement.Obj) { return aFn.call( aQueueElement.Obj, aQueueElement.Param ); } else { return aFn( aQueueElement.Param ); } } CAsync.prototype.OnTimer = function () { //console.log( 'CAsync.OnTimer' ); if (this.Timer) clearTimeout( this.Timer ); this.Timer = null; this.BeginQueue(); var qe = this.RunningQueue.Head; if (!qe) return false; try { var doRepeat = false; doRepeat = this.CallFn( qe.Fn, qe ); doRepeat = doRepeat || false; if (qe.AfterCallCB && !doRepeat) { try { this.CallFn( qe.AfterCallCB, qe ); } catch(err) { } } this.EndQueue( doRepeat ); if (this.Timer) clearTimeout( this.Timer ); this.Timer = null; if (this.SyncMode) { return true; } else { this.StartAsyncCalls(); } } catch(err) { if (qe.AfterCallCB) { try { this.CallFn( qe.AfterCallCB, qe ); } catch(err) { } } var me = this; if (!this.OnException || (this.OnException && this.OnException(err,me))) { this.Stop(); throw err; } } return false; } // Public Functions CAsync.prototype.CallObj = function( aObj, aFn, aParam, aBeforeCallCB, aAfterCallCB ) { // aFn, aBeforeCallCB, aAfterCallCB = // doRepeat = aObj.function( aParam ) or var qe = this.NewQueueElement( aObj, aFn, aParam, 0, aBeforeCallCB, aAfterCallCB ); this.AppendQueueElement( this.WaitingQueue, qe ); this.StartAsyncCalls(); } CAsync.prototype.Call = function( aFn, aParam, aBeforeCallCB, aAfterCallCB ) { // aFn, aBeforeCallCB, aAfterCallCB = // doRepeat = function( aParam ) this.CallObj( null, aFn, aParam, aBeforeCallCB, aAfterCallCB ); } CAsync.prototype.CallObjDefered = function( aDelay, aObj, aFn, aParam, aBeforeCallCB, aAfterCallCB ) { // aFn, aBeforeCallCB, aAfterCallCB = // doRepeat = aObj.function( aParam ) or var qe = this.NewQueueElement( aObj, aFn, aParam, aDelay, aBeforeCallCB, aAfterCallCB ); this.AppendQueueElement( this.WaitingQueue, qe ); this.StartAsyncCalls(); } CAsync.prototype.CallDefered = function( aDelay, aFn, aParam, aBeforeCallCB, aAfterCallCB ) { // aFn, aBeforeCallCB, aAfterCallCB = // doRepeat = function( aParam ) this.CallObjDefered( null, aFn, aParam, aDelay, aBeforeCallCB, aAfterCallCB ); } CAsync.prototype.Stop = function( ) { if (this.Timer) clearTimeout( this.Timer ); this.Timer = null; this.ClearQueue( this.WaitingQueue ); this.ClearQueue( this.RunningQueue ); } CAsync.prototype.StartTimer = function( ) { this.TimerStartTime = new Date().getTime(); } CAsync.prototype.IsTimerExpired = function( aTimeMS ) { if (this.SyncMode || this.Debug) { return false; } aTimeMS = aTimeMS || this.RunTime; var timeNow = new Date().getTime(); return ((timeNow - this.TimerStartTime) > aTimeMS); } CAsync.prototype.GetElapsedTime = function( ) { if (this.SyncMode || this.Debug) { return 0; } var timeNow = new Date().getTime(); return timeNow - this.TimerStartTime; } // Public Object var Async = new CAsync();