Skip to content

Commit 34434cf

Browse files
jbedardgkalpak
authored andcommitted
refactor($q): separate Promise from Deferred
Closes angular#15064 BREAKING CHANGE: Previously, the `Deferred` object returned by `$q.defer()` delegated the `resolve()`, `reject()` and `notify()` methods to `Deferred.prototype`. Thus, it was possible to modify `Deferred.prototype` and have the changes reflect to all `Deferred` objects. This commit removes that delegation, so modifying the above three methods on `Deferred.prototype` will no longer have an effect on `Deferred` objects.
1 parent cdf3d5e commit 34434cf

File tree

1 file changed

+97
-103
lines changed

1 file changed

+97
-103
lines changed

src/ng/q.js

+97-103
Original file line numberDiff line numberDiff line change
@@ -299,14 +299,18 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
299299
* @returns {Deferred} Returns a new instance of deferred.
300300
*/
301301
function defer() {
302-
var d = new Deferred();
303-
//Necessary to support unbound execution :/
304-
d.resolve = simpleBind(d, d.resolve);
305-
d.reject = simpleBind(d, d.reject);
306-
d.notify = simpleBind(d, d.notify);
307-
return d;
302+
return new Deferred();
308303
}
309304

305+
function Deferred() {
306+
var promise = this.promise = new Promise();
307+
//Non prototype methods necessary to support unbound execution :/
308+
this.resolve = function(val) { resolvePromise(promise, val); };
309+
this.reject = function(reason) { rejectPromise(promise, reason); };
310+
this.notify = function(progress) { notifyPromise(promise, progress); };
311+
}
312+
313+
310314
function Promise() {
311315
this.$$state = { status: 0 };
312316
}
@@ -316,13 +320,13 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
316320
if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
317321
return this;
318322
}
319-
var result = new Deferred();
323+
var result = new Promise();
320324

321325
this.$$state.pending = this.$$state.pending || [];
322326
this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
323327
if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
324328

325-
return result.promise;
329+
return result;
326330
},
327331

328332
'catch': function(callback) {
@@ -338,34 +342,27 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
338342
}
339343
});
340344

341-
//Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
342-
function simpleBind(context, fn) {
343-
return function(value) {
344-
fn.call(context, value);
345-
};
346-
}
347-
348345
function processQueue(state) {
349-
var fn, deferred, pending;
346+
var fn, promise, pending;
350347

351348
pending = state.pending;
352349
state.processScheduled = false;
353350
state.pending = undefined;
354351
try {
355352
for (var i = 0, ii = pending.length; i < ii; ++i) {
356353
state.pur = true;
357-
deferred = pending[i][0];
354+
promise = pending[i][0];
358355
fn = pending[i][state.status];
359356
try {
360357
if (isFunction(fn)) {
361-
deferred.resolve(fn(state.value));
358+
resolvePromise(promise, fn(state.value));
362359
} else if (state.status === 1) {
363-
deferred.resolve(state.value);
360+
resolvePromise(promise, state.value);
364361
} else {
365-
deferred.reject(state.value);
362+
rejectPromise(promise, state.value);
366363
}
367364
} catch (e) {
368-
deferred.reject(e);
365+
rejectPromise(promise, e);
369366
}
370367
}
371368
} finally {
@@ -401,83 +398,80 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
401398
nextTick(function() { processQueue(state); });
402399
}
403400

404-
function Deferred() {
405-
this.promise = new Promise();
401+
function resolvePromise(promise, val) {
402+
if (promise.$$state.status) return;
403+
if (val === promise) {
404+
$$reject(promise, $qMinErr(
405+
'qcycle',
406+
'Expected promise to be resolved with value other than itself \'{0}\'',
407+
val));
408+
} else {
409+
$$resolve(promise, val);
410+
}
411+
406412
}
407413

408-
extend(Deferred.prototype, {
409-
resolve: function(val) {
410-
if (this.promise.$$state.status) return;
411-
if (val === this.promise) {
412-
this.$$reject($qMinErr(
413-
'qcycle',
414-
'Expected promise to be resolved with value other than itself \'{0}\'',
415-
val));
414+
function $$resolve(promise, val) {
415+
var then;
416+
var done = false;
417+
try {
418+
if (isObject(val) || isFunction(val)) then = val.then;
419+
if (isFunction(then)) {
420+
promise.$$state.status = -1;
421+
then.call(val, doResolve, doReject, doNotify);
416422
} else {
417-
this.$$resolve(val);
418-
}
419-
},
420-
421-
$$resolve: function(val) {
422-
var then;
423-
var that = this;
424-
var done = false;
425-
try {
426-
if (isObject(val) || isFunction(val)) then = val.then;
427-
if (isFunction(then)) {
428-
this.promise.$$state.status = -1;
429-
then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
430-
} else {
431-
this.promise.$$state.value = val;
432-
this.promise.$$state.status = 1;
433-
scheduleProcessQueue(this.promise.$$state);
434-
}
435-
} catch (e) {
436-
rejectPromise(e);
423+
promise.$$state.value = val;
424+
promise.$$state.status = 1;
425+
scheduleProcessQueue(promise.$$state);
437426
}
427+
} catch (e) {
428+
doReject(e);
429+
}
438430

439-
function resolvePromise(val) {
440-
if (done) return;
441-
done = true;
442-
that.$$resolve(val);
443-
}
444-
function rejectPromise(val) {
445-
if (done) return;
446-
done = true;
447-
that.$$reject(val);
448-
}
449-
},
431+
function doResolve(val) {
432+
if (done) return;
433+
done = true;
434+
$$resolve(promise, val);
435+
}
436+
function doReject(val) {
437+
if (done) return;
438+
done = true;
439+
$$reject(promise, val);
440+
}
441+
function doNotify(progress) {
442+
notifyPromise(promise, progress);
443+
}
444+
}
450445

451-
reject: function(reason) {
452-
if (this.promise.$$state.status) return;
453-
this.$$reject(reason);
454-
},
446+
function rejectPromise(promise, reason) {
447+
if (promise.$$state.status) return;
448+
$$reject(promise, reason);
449+
}
455450

456-
$$reject: function(reason) {
457-
this.promise.$$state.value = reason;
458-
this.promise.$$state.status = 2;
459-
scheduleProcessQueue(this.promise.$$state);
460-
},
451+
function $$reject(promise, reason) {
452+
promise.$$state.value = reason;
453+
promise.$$state.status = 2;
454+
scheduleProcessQueue(promise.$$state);
455+
}
461456

462-
notify: function(progress) {
463-
var callbacks = this.promise.$$state.pending;
464-
465-
if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
466-
nextTick(function() {
467-
var callback, result;
468-
for (var i = 0, ii = callbacks.length; i < ii; i++) {
469-
result = callbacks[i][0];
470-
callback = callbacks[i][3];
471-
try {
472-
result.notify(isFunction(callback) ? callback(progress) : progress);
473-
} catch (e) {
474-
exceptionHandler(e);
475-
}
457+
function notifyPromise(promise, progress) {
458+
var callbacks = promise.$$state.pending;
459+
460+
if ((promise.$$state.status <= 0) && callbacks && callbacks.length) {
461+
nextTick(function() {
462+
var callback, result;
463+
for (var i = 0, ii = callbacks.length; i < ii; i++) {
464+
result = callbacks[i][0];
465+
callback = callbacks[i][3];
466+
try {
467+
notifyPromise(result, isFunction(callback) ? callback(progress) : progress);
468+
} catch (e) {
469+
exceptionHandler(e);
476470
}
477-
});
478-
}
471+
}
472+
});
479473
}
480-
});
474+
}
481475

482476
/**
483477
* @ngdoc method
@@ -516,9 +510,9 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
516510
* @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
517511
*/
518512
function reject(reason) {
519-
var result = new Deferred();
520-
result.reject(reason);
521-
return result.promise;
513+
var result = new Promise();
514+
rejectPromise(result, reason);
515+
return result;
522516
}
523517

524518
function handleCallback(value, resolver, callback) {
@@ -556,9 +550,9 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
556550

557551

558552
function when(value, callback, errback, progressBack) {
559-
var result = new Deferred();
560-
result.resolve(value);
561-
return result.promise.then(callback, errback, progressBack);
553+
var result = new Promise();
554+
resolvePromise(result, value);
555+
return result.then(callback, errback, progressBack);
562556
}
563557

564558
/**
@@ -594,25 +588,25 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
594588
*/
595589

596590
function all(promises) {
597-
var deferred = new Deferred(),
591+
var result = new Promise(),
598592
counter = 0,
599593
results = isArray(promises) ? [] : {};
600594

601595
forEach(promises, function(promise, key) {
602596
counter++;
603597
when(promise).then(function(value) {
604598
results[key] = value;
605-
if (!(--counter)) deferred.resolve(results);
599+
if (!(--counter)) resolvePromise(result, results);
606600
}, function(reason) {
607-
deferred.reject(reason);
601+
rejectPromise(result, reason);
608602
});
609603
});
610604

611605
if (counter === 0) {
612-
deferred.resolve(results);
606+
resolvePromise(result, results);
613607
}
614608

615-
return deferred.promise;
609+
return result;
616610
}
617611

618612
/**
@@ -644,19 +638,19 @@ function qFactory(nextTick, exceptionHandler, errorOnUnhandledRejections) {
644638
throw $qMinErr('norslvr', 'Expected resolverFn, got \'{0}\'', resolver);
645639
}
646640

647-
var deferred = new Deferred();
641+
var promise = new Promise();
648642

649643
function resolveFn(value) {
650-
deferred.resolve(value);
644+
resolvePromise(promise, value);
651645
}
652646

653647
function rejectFn(reason) {
654-
deferred.reject(reason);
648+
rejectPromise(promise, reason);
655649
}
656650

657651
resolver(resolveFn, rejectFn);
658652

659-
return deferred.promise;
653+
return promise;
660654
}
661655

662656
// Let's make the instanceof operator work for promises, so that

0 commit comments

Comments
 (0)