Last active
December 14, 2015 19:49
-
-
Save ysugimoto/5139885 to your computer and use it in GitHub Desktop.
Deferred pattern implements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(module) { | |
"use strict"; | |
// Scope stacks | |
var LAZY_ID = 0, | |
PARALLELS = []; | |
// exports | |
module.Lazy = Lazy; | |
// Check variable is Function | |
function isF(fn) { | |
return typeof fn === 'function'; | |
} | |
// Get status string from status code | |
function getStatusString(status) { | |
return ( status === Lazy.SUCCESS ) ? 'success' : | |
( status === Lazy.FAIED ) ? 'failed' : | |
( status === Lazy.ABORT ) ? 'abort' : | |
void 0; | |
} | |
// Status change class | |
function Signal(lazy) { | |
this.lazy = lazy; | |
} | |
Signal.prototype = { | |
constructor: Signal, | |
/** | |
* Change status to "success" if allowed | |
* @access public | |
* @param mixed message | |
*/ | |
success: function(message) { | |
this.send(Lazy.SUCCESS, message); | |
}, | |
/** | |
* Change status to "failed" if allowed | |
* @access public | |
* @param mixed message | |
*/ | |
failed: function(message) { | |
this.send(Lazy.FAILED, message); | |
}, | |
/** | |
* Change status to "abort" if alloed | |
* @access public | |
* @param mixed message | |
*/ | |
abort: function(message) { | |
this.send(Lazy.ABORT, message); | |
}, | |
/** | |
* Send a signal | |
* @access public | |
* @param int status | |
*/ | |
send: function(status, message) { | |
if ( ! this.lazy.isPromised() ) { | |
this.lazy._changeStatus(status, message); | |
} | |
} | |
}; | |
/** | |
* Lazy class | |
*/ | |
function Lazy() { | |
// Initial parameters | |
this._promised = false; | |
this._proceeded = false; | |
this._appointID = 0; | |
this._status = Lazy.SUSPEND; | |
this._message = void 0; | |
this._pipes = { 0x01: null, 0x10: null, 0x11: null }; | |
this._callbacks = { 0x01: [], 0x10: [], 0x11: [] }; | |
this._chains = { 0x01: null, 0x10: null, 0x11: null }; | |
// Attach signal class instance | |
this.signal = new Signal(this); | |
} | |
// Static status properties | |
Lazy.SUSPEND = 0x00; | |
Lazy.SUCCESS = 0x01; | |
Lazy.FAILED = 0x10; | |
Lazy.ABORT = 0x11; | |
// Methos implements | |
Lazy.prototype = { | |
constructor: Lazy, | |
/** | |
* Promise this lazy object | |
* @access public | |
* @return this | |
*/ | |
promise: function() { | |
this._promised = true; | |
return this; | |
}, | |
/** | |
* Returms lazy object is promised | |
* @access public | |
* @return boolean | |
*/ | |
isPromised: function() { | |
return this._promised; | |
}, | |
/** | |
* Regist status hooks functions | |
* @access public | |
* @param function success | |
* @param function failed | |
* @param function abort | |
* @return this | |
*/ | |
pipe: function(success, failed, abort) { | |
this._pipes[Lazy.SUCCESS] = success; | |
this._pipes[Lazy.FAILED] = failed; | |
this._pipes[Lazy.ABORT] = abort; | |
return this; | |
}, | |
/** | |
* Attach success callback function | |
* @access public | |
* @param function callback | |
* @return this | |
*/ | |
success: function(callback) { | |
this._callbacks[Lazy.SUCCESS].push([callback, 0]); | |
return ( this.isSuccess() ) ? this_execute() : this.chain(Lazy.SUCCESS); | |
}, | |
/** | |
* Returns Lazy status is Success | |
* @access public | |
* @return boolean | |
*/ | |
isSuccess: function() { | |
return this._status === Lazy.SUCCESS; | |
}, | |
/** | |
* Attach failed callback fucntion | |
* @access public | |
* @param function callback | |
* @return this | |
*/ | |
failed: function(callback) { | |
this._callbacks[Lazy.FAILED].push([callback, 0]); | |
return ( this.isFailed() ) ? this._execute() : this.chain(Lazy.FAILED); | |
}, | |
/** | |
* Returns Lazy status is Failed | |
* @access public | |
* @return boolean | |
*/ | |
isFailed: function() { | |
return this._status === Lazy.FAILED; | |
}, | |
/** | |
* Attach abort callback function | |
* @access public | |
* @param function callback | |
* @return this | |
*/ | |
abort: function(callback) { | |
this._callbacks[Lazy.ABORT].push([callback, 0]); | |
return ( this.isAbort() ) ? this._execute() : this.chain(Lazy.ABORT); | |
}, | |
/** | |
* Returns Lazy status is Abort | |
* @access public | |
* @return boolean | |
*/ | |
isAbort: function() { | |
return this._status === Lazy.ABORT; | |
}, | |
/** | |
* Returns chained "promised" Lazy instance | |
* @access public | |
* @param string type | |
* @return Lazy | |
*/ | |
chain: function(type) { | |
this._chains[type] = new Lazy(); | |
return this._chains[type]; | |
}, | |
/** | |
* Set Parallel appointment ID | |
* @access public | |
* @param number appointID | |
* @return void | |
*/ | |
setAppointID: function(appointID) { | |
this._appointID = appointID; | |
}, | |
/** | |
* Get sended message data | |
* @access public | |
* @return mixed | |
*/ | |
getMessage: function() { | |
return this._message; | |
}, | |
/** | |
* Change status | |
* @access protected on this scope | |
* @param int status | |
* @param mixed message | |
* @return void | |
*/ | |
_changeStatus: function(status, message) { | |
// If state set true either, nothing to do. | |
if ( this._proceeded ) { | |
return; | |
} | |
// Mark proceeded | |
this._proceeded = true; | |
// Change status | |
this._status = status; | |
this._message = message; | |
// Execute | |
this._execute(); | |
}, | |
/** | |
* Execute registed callbacks | |
* @access private | |
* @param boolean pipesCallback | |
* @return void | |
*/ | |
_execute: function(pipesCallback) { | |
var that = this, | |
idx = -1, | |
status = this._status, | |
callbacks = this._callbacks[status] || [], | |
message = ( isF(this._pipes[status]) && ! pipesCallback ) | |
? this._pipes[status](this._message) | |
: this._message; | |
// Does pipe function returns Lazy instance? | |
if ( message instanceof Lazy ) { | |
// More Lazy... | |
return message[getStatusString(status)](function(msg) { | |
that._message = message.getMessage(); | |
that._execute(true); | |
}); | |
} | |
while ( callbacks[++idx] ) { | |
isF(callbacks[idx]) && callbacks[idx](message); | |
} | |
// Does this object has appointment? | |
if ( this._appointID > 0 && this._appointID in PARALLELS ) { | |
PARALLELS[this._appointID].signal.send(status, message); | |
} | |
// Messaging chain lazy | |
this._chains[status] && this._chains[status].signal.send(status, message); | |
}, | |
/** | |
* Create parallel Lazy | |
* @access public | |
* @return Lazy.Parallel | |
*/ | |
parallel: function() { | |
return new Lazy.Parallel(this); | |
} | |
}; | |
/** | |
* Lazy.Parallel: parallel async class | |
*/ | |
Lazy.Parallel = function() { | |
var idx = -1; | |
this._appointID = ++LAZY_ID; | |
this._lazy = []; | |
this._proceeded = false; | |
this._promised = false; | |
this._status = Lazy.PENDING; | |
this._message = void 0; | |
this._pipes = { 0x01: null, 0x10: null, 0x11: null }; | |
this._callbacks = { 0x01: [], 0x10: [], 0x11: [] }; | |
this._chains = { 0x01: null, 0x10: null, 0x11: null }; | |
this.signal = new Signal(this); | |
// Add lazy list from arguments | |
while( arguments[++idx] ) { | |
if ( arguments[idx] instanceof Lazy ) { | |
arguments[idx].setAppointID(this._appointID); | |
this._lazy[this._lazy.length] = arguments[idx]; | |
} | |
} | |
// Add parallel stack | |
PARALLELS[this._appointID] = this; | |
return this; | |
} | |
/** | |
* Method implements | |
*/ | |
Lazy.Parallel.prototype = { | |
constructor: Lazy.Parallel, | |
// Attach Lazy prototypes | |
promise: Lazy.prototype.promise, | |
iSPromised: Lazy.prototype.isPromised, | |
success: Lazy.prototype.success, | |
isSuccess: Lazy.prototype.isSuccess, | |
failed: Lazy.prototype.failed, | |
isFailed: Lazy.prototype.isFailed, | |
abort: Lazy.prototype.abort, | |
isAbort: Lazy.prototype.isAbort, | |
pipe: Lazy.prototype.pipe, | |
getMessage: Lazy.prototype.getMessage, | |
/** | |
* Add parallel lazy instance list | |
* @access public | |
* @param Lazy lazy | |
* @return this | |
* @throws TypeError | |
*/ | |
append: function(lazy) { | |
if ( lazy instanceof Lazy ) { | |
lazy.setAppointID(this._appointID); | |
this._lazy[this._lazy.length] = lazy; | |
return this; | |
} | |
throw new TypeError('Argument must be Lazy instance on Lazy.Parallel.append'); | |
}, | |
/** | |
* Change status | |
* @access public | |
* @param Number status | |
*/ | |
_changeStatus: function(status, message, pipesCallback) { | |
var flag = true; | |
// Guard already proceeded | |
if ( this._proceeded ) { | |
return; | |
} | |
switch ( status ) { | |
case Lazy.SUCCESS: | |
// Do callback if all green | |
while ( this._lazy[++idx] ) { | |
if ( this._lazy[idx].status != Lazy.SUCCESS ) { | |
flag = false; | |
} | |
} | |
break; | |
default : | |
flag = false; | |
// Do callback if either failed flag is true | |
while ( this._lazy[++idx] ) { | |
if ( this._lazy[idx].status == Lazy[status] ) { | |
flag = true; | |
break; | |
} | |
} | |
break; | |
} | |
// Execute callbacks if flag is true | |
if ( flag === true ) { | |
// Double guard | |
this._proceeded = true; | |
this._status = status; | |
this._message = message; | |
this._execute(); | |
} | |
}, | |
/** | |
* Execute callbacks | |
* @access private | |
* @param boolean pipesCallback | |
*/ | |
_execute: function(pipesCallback) { | |
var that = this, | |
idx = -1, | |
callbacks = this._callbacks[status] || [], | |
message = ( isF(this._pipes[status]) && ! pipesCallback ) | |
? this._pipes[status](this._message) | |
: this._message; | |
// Does pipe function returns Lazy instance? | |
if ( message instanceof Lazy ) { | |
// More Lazy... | |
return message[getStatusString(status)](function(msg) { | |
that._message = message.getMessage(); | |
that._execute(true); | |
}); | |
} | |
while ( callbacks[++idx] ) { | |
isF(callbacks[idx]) && callbacks[idx](message); | |
} | |
// Remove stack ( self object reference ) | |
delete PARALLELS[this._appointID]; | |
} | |
}; | |
})(typeof Module !== 'undefined' ? Module : this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment