hdjs
Version:
hdjs framework
321 lines (280 loc) • 11.8 kB
JavaScript
/**
* 直接来源于jquery的代码。
* @fileOverview Promise/A+
* @beta
*/
define([
'./dollar'
], function( $ ) {
var api;
// 简单版Callbacks, 默认memory,可选once.
function Callbacks( once ) {
var list = [],
stack = !once && [],
fire = function( data ) {
memory = data;
fired = true;
firingIndex = firingStart || 0;
firingStart = 0;
firingLength = list.length;
firing = true;
for ( ; list && firingIndex < firingLength; firingIndex++ ) {
list[ firingIndex ].apply( data[ 0 ], data[ 1 ] );
}
firing = false;
if ( list ) {
if ( stack ) {
stack.length && fire( stack.shift() );
} else {
list = [];
}
}
},
self = {
add: function() {
if ( list ) {
var start = list.length;
(function add ( args ) {
$.each( args, function( _, arg ) {
var type = $.type( arg );
if ( type === 'function' ) {
list.push( arg );
} else if ( arg && arg.length &&
type !== 'string' ) {
add( arg );
}
});
})( arguments );
if ( firing ) {
firingLength = list.length;
} else if ( memory ) {
firingStart = start;
fire( memory );
}
}
return this;
},
disable: function() {
list = stack = memory = undefined;
return this;
},
// Lock the list in its current state
lock: function() {
stack = undefined;
if ( !memory ) {
self.disable();
}
return this;
},
fireWith: function( context, args ) {
if ( list && (!fired || stack) ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
if ( firing ) {
stack.push( args );
} else {
fire( args );
}
}
return this;
},
fire: function() {
self.fireWith( this, arguments );
return this;
}
},
fired, firing, firingStart, firingLength, firingIndex, memory;
return self;
}
function Deferred( func ) {
var tuples = [
// action, add listener, listener list, final state
[ 'resolve', 'done', Callbacks( true ), 'resolved' ],
[ 'reject', 'fail', Callbacks( true ), 'rejected' ],
[ 'notify', 'progress', Callbacks() ]
],
state = 'pending',
promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return Deferred(function( newDefer ) {
$.each( tuples, function( i, tuple ) {
var action = tuple[ 0 ],
fn = $.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for
// forwarding actions to newDefer
deferred[ tuple[ 1 ] ](function() {
var returned;
returned = fn && fn.apply( this, arguments );
if ( returned &&
$.isFunction( returned.promise ) ) {
returned.promise()
.done( newDefer.resolve )
.fail( newDefer.reject )
.progress( newDefer.notify );
} else {
newDefer[ action + 'With' ](
this === promise ?
newDefer.promise() :
this,
fn ? [ returned ] : arguments );
}
});
});
fns = null;
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
return obj != null ? $.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat
promise.pipe = promise.then;
// Add list-specific methods
$.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
promise[ tuple[ 1 ] ] = list.add;
// Handle state
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
// deferred[ resolve | reject | notify ]
deferred[ tuple[ 0 ] ] = function() {
deferred[ tuple[ 0 ] + 'With' ]( this === deferred ? promise :
this, arguments );
return this;
};
deferred[ tuple[ 0 ] + 'With' ] = list.fireWith;
});
// Make the deferred a promise
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
}
api = {
/**
* 创建一个[Deferred](http://api.jquery.com/category/deferred-object/)对象。
* 详细的Deferred用法说明,请参照jQuery的API文档。
*
* Deferred对象在钩子回掉函数中经常要用到,用来处理需要等待的异步操作。
*
* @for Base
* @method Deferred
* @grammar Base.Deferred() => Deferred
* @example
* // 在文件开始发送前做些异步操作。
* // WebUploader会等待此异步操作完成后,开始发送文件。
* Uploader.register({
* 'before-send-file': 'doSomthingAsync'
* }, {
*
* doSomthingAsync: function() {
* var deferred = Base.Deferred();
*
* // 模拟一次异步操作。
* setTimeout(deferred.resolve, 2000);
*
* return deferred.promise();
* }
* });
*/
Deferred: Deferred,
/**
* 判断传入的参数是否为一个promise对象。
* @method isPromise
* @grammar Base.isPromise( anything ) => Boolean
* @param {*} anything 检测对象。
* @return {Boolean}
* @for Base
* @example
* console.log( Base.isPromise() ); // => false
* console.log( Base.isPromise({ key: '123' }) ); // => false
* console.log( Base.isPromise( Base.Deferred().promise() ) ); // => true
*
* // Deferred也是一个Promise
* console.log( Base.isPromise( Base.Deferred() ) ); // => true
*/
isPromise: function( anything ) {
return anything && typeof anything.then === 'function';
},
/**
* 返回一个promise,此promise在所有传入的promise都完成了后完成。
* 详细请查看[这里](http://api.jquery.com/jQuery.when/)。
*
* @method when
* @for Base
* @grammar Base.when( promise1[, promise2[, promise3...]] ) => Promise
*/
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
slice = [].slice,
resolveValues = slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || (subordinate &&
$.isFunction( subordinate.promise )) ? length : 0,
// the master Deferred. If resolveValues consist of
// only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ?
slice.call( arguments ) : value;
if ( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !(--remaining) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] &&
$.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts,
resolveValues ) )
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts,
progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
};
return api;
});