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

Commit 664526d

Browse files
fix($q): call reject() even if $exceptionHandler rethrows
Normally $exceptionHandler doesn't throw an exception. It is normally used just for logging and so on. But if an application developer implemented a version that did throw an exception then $q would never have called reject() when converting an exception thrown inside a `then` handler into a rejected promise.
1 parent a644ca7 commit 664526d

File tree

2 files changed

+52
-3
lines changed

2 files changed

+52
-3
lines changed

src/ng/q.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -237,17 +237,17 @@ function qFactory(nextTick, exceptionHandler) {
237237
try {
238238
result.resolve((callback || defaultCallback)(value));
239239
} catch(e) {
240-
exceptionHandler(e);
241240
result.reject(e);
241+
exceptionHandler(e);
242242
}
243243
};
244244

245245
var wrappedErrback = function(reason) {
246246
try {
247247
result.resolve((errback || defaultErrback)(reason));
248248
} catch(e) {
249-
exceptionHandler(e);
250249
result.reject(e);
250+
exceptionHandler(e);
251251
}
252252
};
253253

test/ng/qSpec.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ describe('q', function() {
160160
mockNextTick.queue.push(task);
161161
},
162162
queue: [],
163+
logExceptions: true,
163164
flush: function() {
164165
if (!mockNextTick.queue.length) throw new Error('Nothing to be flushed!');
165166
while (mockNextTick.queue.length) {
@@ -169,7 +170,9 @@ describe('q', function() {
169170
try {
170171
task();
171172
} catch(e) {
172-
dump('exception in mockNextTick:', e, e.name, e.message, task);
173+
if ( mockNextTick.logExceptions ) {
174+
dump('exception in mockNextTick:', e, e.name, e.message, task);
175+
}
173176
}
174177
});
175178
}
@@ -1393,4 +1396,50 @@ describe('q', function() {
13931396
});
13941397
});
13951398
});
1399+
1400+
1401+
describe('when exceptionHandler rethrows exceptions, ', function() {
1402+
var originalLogExceptions, deferred, errorSpy, exceptionExceptionSpy;
1403+
1404+
beforeEach(function() {
1405+
// Turn off exception logging for these particular tests
1406+
originalLogExceptions = mockNextTick.logExceptions;
1407+
mockNextTick.logExceptions = false;
1408+
1409+
// Set up spies
1410+
exceptionExceptionSpy = jasmine.createSpy('rethrowExceptionHandler')
1411+
.andCallFake(function rethrowExceptionHandler(e) {
1412+
throw e;
1413+
});
1414+
errorSpy = jasmine.createSpy('errorSpy');
1415+
1416+
1417+
q = qFactory(mockNextTick.nextTick, exceptionExceptionSpy);
1418+
deferred = q.defer();
1419+
});
1420+
1421+
1422+
afterEach(function() {
1423+
// Restore the original exception logging mode
1424+
mockNextTick.logExceptions = originalLogExceptions;
1425+
});
1426+
1427+
1428+
it('should still reject the promise, when exception is thrown in success handler, even if exceptionHandler rethrows', function() {
1429+
deferred.promise.then(function() { throw 'reject'; }).then(null, errorSpy);
1430+
deferred.resolve('resolve');
1431+
mockNextTick.flush();
1432+
expect(exceptionExceptionSpy).toHaveBeenCalled();
1433+
expect(errorSpy).toHaveBeenCalled();
1434+
});
1435+
1436+
1437+
it('should still reject the promise, when exception is thrown in success handler, even if exceptionHandler rethrows', function() {
1438+
deferred.promise.then(null, function() { throw 'reject again'; }).then(null, errorSpy);
1439+
deferred.reject('reject');
1440+
mockNextTick.flush();
1441+
expect(exceptionExceptionSpy).toHaveBeenCalled();
1442+
expect(errorSpy).toHaveBeenCalled();
1443+
});
1444+
});
13961445
});

0 commit comments

Comments
 (0)