Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 23bc92b

Browse files
committed
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
1 parent 48b34dd commit 23bc92b

File tree

1 file changed

+128
-121
lines changed

1 file changed

+128
-121
lines changed

src/ng/q.js

+128-121
Original file line numberDiff line numberDiff line change
@@ -247,143 +247,122 @@ function qFactory(nextTick, exceptionHandler) {
247247
* @returns {Deferred} Returns a new instance of deferred.
248248
*/
249249
var defer = function() {
250-
var pending = [],
251-
value, deferred;
252-
253-
deferred = {
254-
255-
resolve: function(val) {
256-
if (pending) {
257-
var callbacks = pending;
258-
pending = undefined;
259-
value = ref(val);
260-
261-
if (callbacks.length) {
262-
nextTick(function() {
263-
var callback;
264-
for (var i = 0, ii = callbacks.length; i < ii; i++) {
265-
callback = callbacks[i];
266-
value.then(callback[0], callback[1], callback[2]);
267-
}
268-
});
269-
}
270-
}
271-
},
272-
273-
274-
reject: function(reason) {
275-
deferred.resolve(createInternalRejectedPromise(reason));
276-
},
250+
return new Deferred();
251+
};
277252

253+
function Promise () {
254+
this.$$pending = [];
255+
}
278256

279-
notify: function(progress) {
280-
if (pending) {
281-
var callbacks = pending;
257+
Promise.prototype = {
258+
then: function(callback, errback, progressback) {
259+
var result = new Deferred();
282260

283-
if (pending.length) {
284-
nextTick(function() {
285-
var callback;
286-
for (var i = 0, ii = callbacks.length; i < ii; i++) {
287-
callback = callbacks[i];
288-
callback[2](progress);
289-
}
290-
});
291-
}
261+
var wrappedCallback = function(value) {
262+
try {
263+
result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
264+
} catch(e) {
265+
result.reject(e);
266+
exceptionHandler(e);
292267
}
293-
},
294-
295-
296-
promise: {
297-
then: function(callback, errback, progressback) {
298-
var result = defer();
299-
300-
var wrappedCallback = function(value) {
301-
try {
302-
result.resolve((isFunction(callback) ? callback : defaultCallback)(value));
303-
} catch(e) {
304-
result.reject(e);
305-
exceptionHandler(e);
306-
}
307-
};
308-
309-
var wrappedErrback = function(reason) {
310-
try {
311-
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
312-
} catch(e) {
313-
result.reject(e);
314-
exceptionHandler(e);
315-
}
316-
};
317-
318-
var wrappedProgressback = function(progress) {
319-
try {
320-
result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
321-
} catch(e) {
322-
exceptionHandler(e);
323-
}
324-
};
268+
};
269+
270+
var wrappedErrback = function(reason) {
271+
try {
272+
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
273+
} catch(e) {
274+
result.reject(e);
275+
exceptionHandler(e);
276+
}
277+
};
325278

326-
if (pending) {
327-
pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
328-
} else {
329-
value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
330-
}
279+
var wrappedProgressback = function(progress) {
280+
try {
281+
result.notify((isFunction(progressback) ? progressback : defaultCallback)(progress));
282+
} catch(e) {
283+
exceptionHandler(e);
284+
}
285+
};
331286

332-
return result.promise;
333-
},
287+
if (this.$$pending) {
288+
this.$$pending.push([wrappedCallback, wrappedErrback, wrappedProgressback]);
289+
} else {
290+
this.$$value.then(wrappedCallback, wrappedErrback, wrappedProgressback);
291+
}
334292

335-
"catch": function(callback) {
336-
return this.then(null, callback);
337-
},
293+
return result.promise;
294+
},
295+
296+
"catch": function(callback) {
297+
return this.then(null, callback);
298+
},
299+
"finally": function(callback) {
300+
return this.then(function(value) {
301+
return handleCallback(value, true, callback);
302+
}, function(error) {
303+
return handleCallback(error, false, callback);
304+
});
305+
}
306+
};
338307

339-
"finally": function(callback) {
308+
//Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
309+
function simpleBind(context, fn) {
310+
return function(value) {
311+
fn.call(context, value);
312+
};
313+
}
340314

341-
function makePromise(value, resolved) {
342-
var result = defer();
343-
if (resolved) {
344-
result.resolve(value);
345-
} else {
346-
result.reject(value);
347-
}
348-
return result.promise;
349-
}
315+
function Deferred () {
316+
this.promise = new Promise();
317+
//Necessary to support unbound execution :/
318+
this.resolve = simpleBind(this, this.resolve);
319+
this.reject = simpleBind(this, this.reject);
320+
this.notify = simpleBind(this, this.notify);
321+
}
350322

351-
function handleCallback(value, isResolved) {
352-
var callbackOutput = null;
353-
try {
354-
callbackOutput = (callback ||defaultCallback)();
355-
} catch(e) {
356-
return makePromise(e, false);
323+
Deferred.prototype = {
324+
resolve: function(val) {
325+
if (this.promise.$$pending) {
326+
var callbacks = this.promise.$$pending;
327+
this.promise.$$pending = undefined;
328+
this.promise.$$value = ref(val);
329+
330+
if (callbacks.length) {
331+
nextTick(simpleBind(this, function() {
332+
var callback;
333+
for (var i = 0, ii = callbacks.length; i < ii; i++) {
334+
callback = callbacks[i];
335+
this.promise.$$value.then(callback[0], callback[1], callback[2]);
357336
}
358-
if (isPromiseLike(callbackOutput)) {
359-
return callbackOutput.then(function() {
360-
return makePromise(value, isResolved);
361-
}, function(error) {
362-
return makePromise(error, false);
363-
});
364-
} else {
365-
return makePromise(value, isResolved);
337+
}));
338+
}
339+
}
340+
},
341+
reject: function(reason) {
342+
this.resolve(createInternalRejectedPromise(reason));
343+
},
344+
notify: function(progress) {
345+
if (this.promise.$$pending) {
346+
var callbacks = this.promise.$$pending;
347+
348+
if (this.promise.$$pending.length) {
349+
nextTick(function() {
350+
var callback;
351+
for (var i = 0, ii = callbacks.length; i < ii; i++) {
352+
callback = callbacks[i];
353+
callback[2](progress);
366354
}
367-
}
368-
369-
return this.then(function(value) {
370-
return handleCallback(value, true);
371-
}, function(error) {
372-
return handleCallback(error, false);
373355
});
374356
}
375357
}
376-
};
377-
378-
return deferred;
358+
}
379359
};
380360

381-
382361
var ref = function(value) {
383362
if (isPromiseLike(value)) return value;
384363
return {
385364
then: function(callback) {
386-
var result = defer();
365+
var result = new Deferred();
387366
nextTick(function() {
388367
result.resolve(callback(value));
389368
});
@@ -430,15 +409,43 @@ function qFactory(nextTick, exceptionHandler) {
430409
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
431410
*/
432411
var reject = function(reason) {
433-
var result = defer();
412+
var result = new Deferred();
434413
result.reject(reason);
435414
return result.promise;
436415
};
437416

417+
var makePromise = function makePromise(value, resolved) {
418+
var result = new Deferred();
419+
if (resolved) {
420+
result.resolve(value);
421+
} else {
422+
result.reject(value);
423+
}
424+
return result.promise;
425+
};
426+
427+
var handleCallback = function handleCallback(value, isResolved, callback) {
428+
var callbackOutput = null;
429+
try {
430+
callbackOutput = (callback ||defaultCallback)();
431+
} catch(e) {
432+
return makePromise(e, false);
433+
}
434+
if (isPromiseLike(callbackOutput)) {
435+
return callbackOutput.then(function() {
436+
return makePromise(value, isResolved);
437+
}, function(error) {
438+
return makePromise(error, false);
439+
});
440+
} else {
441+
return makePromise(value, isResolved);
442+
}
443+
};
444+
438445
var createInternalRejectedPromise = function(reason) {
439446
return {
440447
then: function(callback, errback) {
441-
var result = defer();
448+
var result = new Deferred();
442449
nextTick(function() {
443450
try {
444451
result.resolve((isFunction(errback) ? errback : defaultErrback)(reason));
@@ -467,7 +474,7 @@ function qFactory(nextTick, exceptionHandler) {
467474
* @returns {Promise} Returns a promise of the passed value or promise
468475
*/
469476
var when = function(value, callback, errback, progressback) {
470-
var result = defer(),
477+
var result = new Deferred(),
471478
done;
472479

473480
var wrappedCallback = function(value) {
@@ -541,7 +548,7 @@ function qFactory(nextTick, exceptionHandler) {
541548
* with the same rejection value.
542549
*/
543550
function all(promises) {
544-
var deferred = defer(),
551+
var deferred = new Deferred(),
545552
counter = 0,
546553
results = isArray(promises) ? [] : {};
547554

@@ -575,7 +582,7 @@ function qFactory(nextTick, exceptionHandler) {
575582
return new Q(resolver);
576583
}
577584

578-
var deferred = defer();
585+
var deferred = new Deferred();
579586

580587
function resolveFn(value) {
581588
deferred.resolve(value);

0 commit comments

Comments
 (0)