Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@ysugimoto
Last active December 14, 2015 19:49
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ysugimoto/5139885 to your computer and use it in GitHub Desktop.
Save ysugimoto/5139885 to your computer and use it in GitHub Desktop.
Deferred pattern implements
(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