mirror of
https://github.com/zhigang1992/angular.js.git
synced 2026-06-11 15:52:50 +08:00
perf($q): move Deferred and Promise methods to prototypes
NOTE: Deferred doesn't get all the advantages of moving methods to the prototype, since the constructor binds instance methods to "this" to support unbounded execution. Closes #8300
This commit is contained in:
249
src/ng/q.js
249
src/ng/q.js
@@ -247,143 +247,122 @@ function qFactory(nextTick, exceptionHandler) {
|
|||||||
* @returns {Deferred} Returns a new instance of deferred.
|
* @returns {Deferred} Returns a new instance of deferred.
|
||||||
*/
|
*/
|
||||||
var defer = function() {
|
var defer = function() {
|
||||||
var pending = [],
|
return new Deferred();
|
||||||
value, deferred;
|
};
|
||||||
|
|
||||||
deferred = {
|
function Promise () {
|
||||||
|
this.$$pending = [];
|
||||||
|
}
|
||||||
|
|
||||||
resolve: function(val) {
|
Promise.prototype = {
|
||||||
if (pending) {
|
then: function(callback, errback, progressback) {
|
||||||
var callbacks = pending;
|
var result = new Deferred();
|
||||||
pending = undefined;
|
|
||||||
value = ref(val);
|
|
||||||
|
|
||||||
if (callbacks.length) {
|
var wrappedCallback = function(value) {
|
||||||
nextTick(function() {
|
try {
|
||||||
var callback;
|
result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
|
||||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
} catch(e) {
|
||||||
callback = callbacks[i];
|
result.reject(e);
|
||||||
value.then(callback[0], callback[1], callback[2]);
|
exceptionHandler(e);
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
var wrappedErrback = function(reason) {
|
||||||
reject: function(reason) {
|
try {
|
||||||
deferred.resolve(createInternalRejectedPromise(reason));
|
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
|
||||||
},
|
} catch(e) {
|
||||||
|
result.reject(e);
|
||||||
|
exceptionHandler(e);
|
||||||
notify: function(progress) {
|
|
||||||
if (pending) {
|
|
||||||
var callbacks = pending;
|
|
||||||
|
|
||||||
if (pending.length) {
|
|
||||||
nextTick(function() {
|
|
||||||
var callback;
|
|
||||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
|
||||||
callback = callbacks[i];
|
|
||||||
callback[2](progress);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
|
var wrappedProgressback = function(progress) {
|
||||||
|
try {
|
||||||
|
result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
|
||||||
|
} catch(e) {
|
||||||
|
exceptionHandler(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
promise: {
|
if (this.$$pending) {
|
||||||
then: function(callback, errback, progressback) {
|
this.$$pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
|
||||||
var result = defer();
|
} else {
|
||||||
|
this.$$value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
|
||||||
|
}
|
||||||
|
|
||||||
var wrappedCallback = function(value) {
|
return result.promise;
|
||||||
try {
|
},
|
||||||
result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
|
|
||||||
} catch(e) {
|
"catch": function(callback) {
|
||||||
result.reject(e);
|
return this.then(null, callback);
|
||||||
exceptionHandler(e);
|
},
|
||||||
|
"finally": function(callback) {
|
||||||
|
return this.then(function(value) {
|
||||||
|
return handleCallback(value, true, callback);
|
||||||
|
}, function(error) {
|
||||||
|
return handleCallback(error, false, callback);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
|
||||||
|
function simpleBind(context, fn) {
|
||||||
|
return function(value) {
|
||||||
|
fn.call(context, value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function Deferred () {
|
||||||
|
this.promise = new Promise();
|
||||||
|
//Necessary to support unbound execution :/
|
||||||
|
this.resolve = simpleBind(this, this.resolve);
|
||||||
|
this.reject = simpleBind(this, this.reject);
|
||||||
|
this.notify = simpleBind(this, this.notify);
|
||||||
|
}
|
||||||
|
|
||||||
|
Deferred.prototype = {
|
||||||
|
resolve: function(val) {
|
||||||
|
if (this.promise.$$pending) {
|
||||||
|
var callbacks = this.promise.$$pending;
|
||||||
|
this.promise.$$pending = undefined;
|
||||||
|
this.promise.$$value = ref(val);
|
||||||
|
|
||||||
|
if (callbacks.length) {
|
||||||
|
nextTick(simpleBind(this, function() {
|
||||||
|
var callback;
|
||||||
|
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||||
|
callback = callbacks[i];
|
||||||
|
this.promise.$$value.then(callback[0], callback[1], callback[2]);
|
||||||
}
|
}
|
||||||
};
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reject: function(reason) {
|
||||||
|
this.resolve(createInternalRejectedPromise(reason));
|
||||||
|
},
|
||||||
|
notify: function(progress) {
|
||||||
|
if (this.promise.$$pending) {
|
||||||
|
var callbacks = this.promise.$$pending;
|
||||||
|
|
||||||
var wrappedErrback = function(reason) {
|
if (this.promise.$$pending.length) {
|
||||||
try {
|
nextTick(function() {
|
||||||
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
|
var callback;
|
||||||
} catch(e) {
|
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||||
result.reject(e);
|
callback = callbacks[i];
|
||||||
exceptionHandler(e);
|
callback[2](progress);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
var wrappedProgressback = function(progress) {
|
|
||||||
try {
|
|
||||||
result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
|
|
||||||
} catch(e) {
|
|
||||||
exceptionHandler(e);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pending) {
|
|
||||||
pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
|
|
||||||
} else {
|
|
||||||
value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result.promise;
|
|
||||||
},
|
|
||||||
|
|
||||||
"catch": function(callback) {
|
|
||||||
return this.then(null, callback);
|
|
||||||
},
|
|
||||||
|
|
||||||
"finally": function(callback) {
|
|
||||||
|
|
||||||
function makePromise(value, resolved) {
|
|
||||||
var result = defer();
|
|
||||||
if (resolved) {
|
|
||||||
result.resolve(value);
|
|
||||||
} else {
|
|
||||||
result.reject(value);
|
|
||||||
}
|
|
||||||
return result.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCallback(value, isResolved) {
|
|
||||||
var callbackOutput = null;
|
|
||||||
try {
|
|
||||||
callbackOutput = (callback ||defaultCallback)();
|
|
||||||
} catch(e) {
|
|
||||||
return makePromise(e, false);
|
|
||||||
}
|
|
||||||
if (isPromiseLike(callbackOutput)) {
|
|
||||||
return callbackOutput.then(function() {
|
|
||||||
return makePromise(value, isResolved);
|
|
||||||
}, function(error) {
|
|
||||||
return makePromise(error, false);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return makePromise(value, isResolved);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.then(function(value) {
|
|
||||||
return handleCallback(value, true);
|
|
||||||
}, function(error) {
|
|
||||||
return handleCallback(error, false);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
return deferred;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
var ref = function(value) {
|
var ref = function(value) {
|
||||||
if (isPromiseLike(value)) return value;
|
if (isPromiseLike(value)) return value;
|
||||||
return {
|
return {
|
||||||
then: function(callback) {
|
then: function(callback) {
|
||||||
var result = defer();
|
var result = new Deferred();
|
||||||
nextTick(function() {
|
nextTick(function() {
|
||||||
result.resolve(callback(value));
|
result.resolve(callback(value));
|
||||||
});
|
});
|
||||||
@@ -430,15 +409,43 @@ function qFactory(nextTick, exceptionHandler) {
|
|||||||
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
|
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
|
||||||
*/
|
*/
|
||||||
var reject = function(reason) {
|
var reject = function(reason) {
|
||||||
var result = defer();
|
var result = new Deferred();
|
||||||
result.reject(reason);
|
result.reject(reason);
|
||||||
return result.promise;
|
return result.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var makePromise = function makePromise(value, resolved) {
|
||||||
|
var result = new Deferred();
|
||||||
|
if (resolved) {
|
||||||
|
result.resolve(value);
|
||||||
|
} else {
|
||||||
|
result.reject(value);
|
||||||
|
}
|
||||||
|
return result.promise;
|
||||||
|
};
|
||||||
|
|
||||||
|
var handleCallback = function handleCallback(value, isResolved, callback) {
|
||||||
|
var callbackOutput = null;
|
||||||
|
try {
|
||||||
|
callbackOutput = (callback ||defaultCallback)();
|
||||||
|
} catch(e) {
|
||||||
|
return makePromise(e, false);
|
||||||
|
}
|
||||||
|
if (isPromiseLike(callbackOutput)) {
|
||||||
|
return callbackOutput.then(function() {
|
||||||
|
return makePromise(value, isResolved);
|
||||||
|
}, function(error) {
|
||||||
|
return makePromise(error, false);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
return makePromise(value, isResolved);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var createInternalRejectedPromise = function(reason) {
|
var createInternalRejectedPromise = function(reason) {
|
||||||
return {
|
return {
|
||||||
then: function(callback, errback) {
|
then: function(callback, errback) {
|
||||||
var result = defer();
|
var result = new Deferred();
|
||||||
nextTick(function() {
|
nextTick(function() {
|
||||||
try {
|
try {
|
||||||
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
|
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
|
||||||
@@ -467,7 +474,7 @@ function qFactory(nextTick, exceptionHandler) {
|
|||||||
* @returns {Promise} Returns a promise of the passed value or promise
|
* @returns {Promise} Returns a promise of the passed value or promise
|
||||||
*/
|
*/
|
||||||
var when = function(value, callback, errback, progressback) {
|
var when = function(value, callback, errback, progressback) {
|
||||||
var result = defer(),
|
var result = new Deferred(),
|
||||||
done;
|
done;
|
||||||
|
|
||||||
var wrappedCallback = function(value) {
|
var wrappedCallback = function(value) {
|
||||||
@@ -541,7 +548,7 @@ function qFactory(nextTick, exceptionHandler) {
|
|||||||
* with the same rejection value.
|
* with the same rejection value.
|
||||||
*/
|
*/
|
||||||
function all(promises) {
|
function all(promises) {
|
||||||
var deferred = defer(),
|
var deferred = new Deferred(),
|
||||||
counter = 0,
|
counter = 0,
|
||||||
results = isArray(promises) ? [] : {};
|
results = isArray(promises) ? [] : {};
|
||||||
|
|
||||||
@@ -575,7 +582,7 @@ function qFactory(nextTick, exceptionHandler) {
|
|||||||
return new Q(resolver);
|
return new Q(resolver);
|
||||||
}
|
}
|
||||||
|
|
||||||
var deferred = defer();
|
var deferred = new Deferred();
|
||||||
|
|
||||||
function resolveFn(value) {
|
function resolveFn(value) {
|
||||||
deferred.resolve(value);
|
deferred.resolve(value);
|
||||||
|
|||||||
Reference in New Issue
Block a user