var __id = 0; // for test
var // Promise methods
promiseMethods = "done fail isResolved isRejected promise then always pipe".split(" "),
// Static reference to slice
sliceDeferred = [].slice;
jQuery.extend({
// Create a simple deferred (one callbacks list)
_Deferred: function() {
debugger;
var callbacks = []; // callbacks list
var fired; // stored [ context , args ]
var firing; // to avoid firing when already doing so
var cancelled; // flag to know if the deferred has been cancelled
// the deferred itself
var deferred = {
// done( f1, f2, ...)
done: function() {
debugger;
if (!cancelled) {
var args = arguments,
i,
length,
elem,
type,
_fired;
if (fired) {
_fired = fired;
fired = 0;
}
for (i = 0, length = args.length; i < length; i++) {
elem = args[i];
type = jQuery.type(elem);
if (type === "array") {
deferred.done.apply(deferred, elem);
} else if (type === "function") {
callbacks.push(elem);
}
}
if (_fired) {
deferred.resolveWith(_fired[0], _fired[1]);
}
}
return this;
},
// resolve with given context and args
resolveWith: function(context, args) {
debugger;
if (!cancelled && !fired && !firing) {
// make sure args are available (#8421)
args = args || [];
firing = 1;
try {
while (callbacks[0]) {
callbacks.shift().apply(context, args);
}
} finally {
fired = [context, args];
firing = 0;
}
}
return this;
},
// resolve with this as context and given arguments
resolve: function() {
debugger;
deferred.resolveWith(this, arguments);
return this;
},
// Has this deferred been resolved?
isResolved: function() {
return !!(firing || fired);
},
// Cancel
cancel: function() {
debugger;
cancelled = 1;
callbacks = [];
return this;
}
};
deferred.id = __id++; // for test
return deferred;
},
// Full fledged deferred (two callbacks list)
Deferred: function(func) {
debugger;
var deferred = jQuery._Deferred();
var failDeferred = jQuery._Deferred();
var promise;
// Add errorDeferred methods, then and promise
jQuery.extend(deferred, {
then: function(doneCallbacks, failCallbacks) {
debugger;
deferred.done(doneCallbacks).fail(failCallbacks);
return this;
},
always: function() {
return deferred.done.apply(deferred, arguments).fail.apply(this, arguments);
},
fail: failDeferred.done, // here is important(把 deferred 導向 failDeferred 的閉包)
rejectWith: failDeferred.resolveWith,
reject: failDeferred.resolve,
isRejected: failDeferred.isResolved,
pipe: function(fnDone, fnFail) {
return jQuery.Deferred(function(newDefer) {
jQuery.each({
done: [fnDone, "resolve"],
fail: [fnFail, "reject"]
}, function(handler, data) {
var fn = data[0],
action = data[1],
returned;
if (jQuery.isFunction(fn)) {
deferred[handler](function() {
returned = fn.apply(this, arguments);
if (returned && jQuery.isFunction(returned.promise)) {
returned.promise().then(newDefer.resolve, newDefer.reject);
} else {
newDefer[action](returned);
}
});
} else {
deferred[handler](newDefer[action]);
}
});
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function(obj) {
debugger;
if (obj == null) {
if (promise) {
return promise;
}
promise = obj = {};
}
var i = promiseMethods.length;
while (i--) {
obj[promiseMethods[i]] = deferred[promiseMethods[i]];
}
return obj;
}
});
debugger;
// Make sure only one callback list will be used
deferred.done(failDeferred.cancel); // 執行環境的閉包屬 deferred
deferred.fail(deferred.cancel); // 執行環境的閉包屬 failDeferred
// Unexpose cancel
delete deferred.cancel;
// Call given func if any
if (func) {
func.call(deferred, deferred);
}
return deferred;
},
// Deferred helper
when: function(firstParam) {
debugger;
var args = arguments,
i = 0,
length = args.length,
count = length,
deferred = length <= 1 && firstParam && jQuery.isFunction(firstParam.promise) ?
firstParam :
jQuery.Deferred();
function resolveFunc(i) {
debugger;
return function(value) {
debugger;
args[i] = arguments.length > 1 ? sliceDeferred.call(arguments, 0) : value;
if (!(--count)) {
// Strange bug in FF4:
// Values changed onto the arguments object sometimes end up as undefined values
// outside the $.when method. Cloning the object into a fresh array solves the issue
deferred.resolveWith(deferred, sliceDeferred.call(args, 0));
}
};
}
if (length > 1) {
for (; i < length; i++) {
if (args[i] && jQuery.isFunction(args[i].promise)) {
args[i].promise().then(resolveFunc(i), deferred.reject);
} else {
--count;
}
}
if (!count) {
deferred.resolveWith(deferred, args);
}
} else if (deferred !== firstParam) {
deferred.resolveWith(deferred, length ? [firstParam] : []);
}
return deferred.promise();
}
});