321 lines
12 KiB
JavaScript
321 lines
12 KiB
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;
|
||
}); |