Created
August 13, 2014 03:53
-
-
Save ZhangSen1/7e0a06dee15e5c0e8f08 to your computer and use it in GitHub Desktop.
JQuery Deferred
This file contains hidden or 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( window ) { | |
var objExtend = function( tObj, sObj ) { | |
for ( var i in sObj ) { | |
if ( typeof sObj[ i ] !== "object" ) { | |
tObj[ i ] = sObj[ i ]; | |
} else if ( sObj[ i ].constructor == Array ) { | |
tObj[ i ] = Object.clone( sObj[ i ] ); | |
} else { | |
tObj[ i ] = tObj[ i ] || {}; | |
Object.extend( tObj[ i ], sObj[ i ] ); | |
} | |
} | |
}; | |
var optionsCache = {}; | |
var createOptions = function( options ) { | |
var object = optionsCache[ options ] = {}, | |
i, | |
length; | |
options = options.split(/\s+/); | |
for ( i = 0, length = options.length; i < length; i++ ) { | |
object[ options[ i ] ] = true; | |
} | |
return object; | |
}; | |
var Callbacks = function( options ){ | |
options = typeof options == "string" ? ( optionsCache[ options ] || createOptions( options ) ) : {}; | |
var //是否正在触发 | |
firing, | |
//记下当前上下文和args,之后每次add都会直接触发 | |
memory, | |
//是否触发完毕 | |
fired, | |
//待触发的回调个数 | |
firingLength, | |
//正在触发的回调index | |
firingIndex, | |
//待触发的第一个回调index | |
firingStart, | |
//存放回调的list | |
list = [], | |
//存放上下文和args | |
stack = !options.once && []; | |
var fire = function( data ) { | |
memory = options.memory && data; | |
fired = true; | |
firingIndex = firingStart || 0; | |
//保证每次调用fire都从0执行 | |
firingStart = 0; | |
//保证每次调用fire都可获得最新的list.length | |
firingLength = list.length; | |
firing = true; | |
for ( ; list && firingIndex < firingLength; firingIndex++ ) { | |
if (list[ firingIndex ].apply(data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ){ | |
memory = false; | |
break; | |
} | |
}; | |
firing = false; | |
if ( list ) { | |
if ( stack ) { | |
if ( stack.length ) { | |
fire( stack.shift() ); | |
} | |
} else if ( memory ) { // (once memory) 进入此分支, 保证之后 add 的 fn 都只执行一次 | |
list = []; | |
} else { // (once), (once stopOnFalse), (once stopOnFalse memory) | |
self.disable(); | |
} | |
} | |
} | |
var self = { | |
add: function(){ | |
if ( list ) { | |
var start = list.length; | |
( function add( args ) { | |
var i, | |
length, | |
type; | |
for ( i = 0, length = args.length; i < length; i++ ) { | |
arg = args[ i ]; | |
type = typeof arg; | |
if ( type === "function" ) { | |
if ( !options.unique || !self.has( arg ) ) { | |
list.push( arg ); | |
} | |
} else if ( arg && arg.length && type !== "string" ) { | |
//递归添加function | |
add( arg ); | |
} | |
}; | |
} )( arguments ); | |
if ( firing ) { | |
firingLength = list.length; | |
}else if ( memory ) { | |
//如果memory模式,改变执行fn的index并立即执行 | |
firingStart = start; | |
fire( memory ); | |
} | |
} | |
return this; | |
}, | |
remove: function() { | |
if ( list ) { | |
for (var m = 0; m < arguments.length; m++) { | |
var arg = arguments[m], index; | |
}; | |
} | |
return this; | |
}, | |
has: function( fn ) { | |
if ( fn ) { | |
for ( var n = 0, tmpFn; ( tmpFn = list[ n ] ) != null; n++ ) { | |
if ( tmpFn == fn ) { | |
return true; | |
} | |
}; | |
return false; | |
} else { | |
return !!( list && list.length ); | |
} | |
}, | |
empty: function(){ | |
list = []; | |
firingLength = 0; | |
return this; | |
}, | |
disable: function() { | |
list = stack = memory = undefined; | |
return this; | |
}, | |
disabled: function() { | |
return !list; | |
}, | |
lock: function() { | |
stack = undefined; | |
if ( !memory ) { | |
self.disable(); | |
} | |
return this; | |
}, | |
locked: function() { | |
return !stack; | |
}, | |
fireWith: function( context, args ) { | |
if ( list && ( !fired || stack ) ) { | |
args = args || []; | |
args = [ context, args.slice ? args.slice() : args ]; | |
//正在执行fn的时候,将 context 和 args 保存 | |
if ( firing ) { | |
stack.push( args ); | |
}else { | |
fire( args ); | |
} | |
} | |
return this; | |
}, | |
fire: function() { | |
self.fireWith( this, arguments ); | |
}, | |
fired: function() { | |
return !!fired; //!!undefined | |
} | |
}; | |
return self; | |
}; | |
var Deferred = function( func ) { | |
var tuples = [ | |
//触发回调, 添加回调, 回调列表, 完成状态 | |
[ "resolve", "done", Callbacks("once memory"), "resolved" ], | |
[ "reject", "fail", Callbacks("once memory"), "rejected"], | |
[ "notify", "progress", Callbacks("memory")] | |
], | |
state = "pending", | |
//通过对外暴露promise防止更改deferred的状态 | |
promise = { | |
state: function() { | |
return state; | |
}, | |
always: function() { | |
deferred.done( arguments ).fail( arguments ); | |
return this; | |
}, | |
then: function() { | |
}, | |
promise: function( obj ){ | |
return obj != null ? objExtend(deferred, promise) : promise; | |
} | |
}, | |
deferred = {}; | |
for ( var i = 0; i < tuples.length; i++ ) { | |
var tuple = tuples[ i ]; | |
//callbacks | |
var list = tuple[ 2 ], | |
//resolved, rejected, undefined | |
stateString = tuple[ 3 ]; | |
//promise[ done | fail | progress ] = list.add | |
//实际上就是 Callbacks.add | |
promise[ tuple[1] ] = list.add; | |
//初始状态 | |
if ( stateString ) { | |
//添加3个fn, changeState, callbacks.disable, callbacks.lock | |
list.add(function() { | |
state = stateString; | |
}, tuples[ i ^ 1 ][2].disable, tuples[ 2 ][ 2 ].lock ); //(i ^ 1) index改变,给resolve添加faillist.disable, 给reject添加donelist.disable | |
} | |
( function( tmpTuple ) { | |
// deferred[ resolve | reject | notify ] | |
deferred[ tmpTuple ] = function() { | |
//deferred[ resolveWith | rejectWith | notifyWith ] => callbacks.fireWith => callbacks.fire | |
deferred[ tmpTuple + "With" ]( this === deferred ? promise : this, arguments ); | |
return this; | |
}; | |
deferred[ tmpTuple + "With" ] = list.fireWith; | |
} )( tuple[ 0 ] ); | |
}; | |
promise.promise( deferred ); | |
if ( func ) { | |
func.call( deferred, deferred ); | |
} | |
return deferred; | |
}; | |
window.Callbacks = Callbacks; | |
window.Deferred = Deferred; | |
}( window ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment