216216 *
217217 * @returns {Promise } The newly created promise.
218218 */
219+ /**
220+ * @ngdoc provider
221+ * @name $qProvider
222+ *
223+ * @description
224+ */
219225function $QProvider ( ) {
220-
226+ var errorOnUnhandledRejections = true ;
221227 this . $get = [ '$rootScope' , '$exceptionHandler' , function ( $rootScope , $exceptionHandler ) {
222228 return qFactory ( function ( callback ) {
223229 $rootScope . $evalAsync ( callback ) ;
224- } , $exceptionHandler ) ;
230+ } , $exceptionHandler , errorOnUnhandledRejections ) ;
225231 } ] ;
232+
233+ /**
234+ * @ngdoc method
235+ * @name $qProvider#errorOnUnhandledRejections
236+ * @kind function
237+ *
238+ * @description
239+ * Retrieves or overrides whether to generate an error when a rejected promise is not handled.
240+ *
241+ * @param {boolean= } value Whether to generate an error when a rejected promise is not handled.
242+ * @returns {boolean|ng.$qProvider } Current value when called without a new value or self for
243+ * chaining otherwise.
244+ */
245+ this . errorOnUnhandledRejections = function ( value ) {
246+ if ( isDefined ( value ) ) {
247+ errorOnUnhandledRejections = value ;
248+ return this ;
249+ } else {
250+ return errorOnUnhandledRejections ;
251+ }
252+ } ;
226253}
227254
228255function $$QProvider ( ) {
256+ var errorOnUnhandledRejections = true ;
229257 this . $get = [ '$browser' , '$exceptionHandler' , function ( $browser , $exceptionHandler ) {
230258 return qFactory ( function ( callback ) {
231259 $browser . defer ( callback ) ;
232- } , $exceptionHandler ) ;
260+ } , $exceptionHandler , errorOnUnhandledRejections ) ;
233261 } ] ;
262+
263+ this . errorOnUnhandledRejections = function ( value ) {
264+ if ( isDefined ( value ) ) {
265+ errorOnUnhandledRejections = value ;
266+ return this ;
267+ } else {
268+ return errorOnUnhandledRejections ;
269+ }
270+ } ;
234271}
235272
236273/**
@@ -239,10 +276,14 @@ function $$QProvider() {
239276 * @param {function(function) } nextTick Function for executing functions in the next turn.
240277 * @param {function(...*) } exceptionHandler Function into which unexpected exceptions are passed for
241278 * debugging purposes.
279+ @ param {=boolean} errorOnUnhandledRejections Whether an error should be generated on unhandled
280+ * promises rejections.
242281 * @returns {object } Promise manager.
243282 */
244- function qFactory ( nextTick , exceptionHandler ) {
283+ function qFactory ( nextTick , exceptionHandler , errorOnUnhandledRejections ) {
245284 var $qMinErr = minErr ( '$q' , TypeError ) ;
285+ var queueSize = 0 ;
286+ var checkQueue = [ ] ;
246287
247288 /**
248289 * @ngdoc method
@@ -307,28 +348,62 @@ function qFactory(nextTick, exceptionHandler) {
307348 pending = state . pending ;
308349 state . processScheduled = false ;
309350 state . pending = undefined ;
310- for ( var i = 0 , ii = pending . length ; i < ii ; ++ i ) {
311- deferred = pending [ i ] [ 0 ] ;
312- fn = pending [ i ] [ state . status ] ;
313- try {
314- if ( isFunction ( fn ) ) {
315- deferred . resolve ( fn ( state . value ) ) ;
316- } else if ( state . status === 1 ) {
317- deferred . resolve ( state . value ) ;
318- } else {
319- deferred . reject ( state . value ) ;
351+ try {
352+ for ( var i = 0 , ii = pending . length ; i < ii ; ++ i ) {
353+ state . pur = true ;
354+ deferred = pending [ i ] [ 0 ] ;
355+ fn = pending [ i ] [ state . status ] ;
356+ try {
357+ if ( isFunction ( fn ) ) {
358+ deferred . resolve ( fn ( state . value ) ) ;
359+ } else if ( state . status === 1 ) {
360+ deferred . resolve ( state . value ) ;
361+ } else {
362+ deferred . reject ( state . value ) ;
363+ }
364+ } catch ( e ) {
365+ deferred . reject ( e ) ;
366+ exceptionHandler ( e ) ;
320367 }
321- } catch ( e ) {
322- deferred . reject ( e ) ;
323- exceptionHandler ( e ) ;
368+ }
369+ } finally {
370+ -- queueSize ;
371+ if ( errorOnUnhandledRejections && queueSize === 0 ) {
372+ nextTick ( processChecksFn ( ) ) ;
373+ }
374+ }
375+ }
376+
377+ function processChecks ( ) {
378+ while ( ! queueSize && checkQueue . length ) {
379+ var toCheck = checkQueue . shift ( ) ;
380+ if ( ! toCheck . pur ) {
381+ toCheck . pur = true ;
382+ var errorMessage = 'Possibly unhandled rejection: ' + toDebugString ( toCheck . value ) ;
383+ exceptionHandler ( errorMessage ) ;
324384 }
325385 }
326386 }
327387
388+ function processChecksFn ( ) {
389+ return function ( ) { processChecks ( ) ; } ;
390+ }
391+
392+ function processQueueFn ( state ) {
393+ return function ( ) { processQueue ( state ) ; } ;
394+ }
395+
328396 function scheduleProcessQueue ( state ) {
397+ if ( errorOnUnhandledRejections && ! state . pending && state . status === 2 && ! state . pur ) {
398+ if ( queueSize === 0 && checkQueue . length === 0 ) {
399+ nextTick ( processChecksFn ( ) ) ;
400+ }
401+ checkQueue . push ( state ) ;
402+ }
329403 if ( state . processScheduled || ! state . pending ) return ;
330404 state . processScheduled = true ;
331- nextTick ( function ( ) { processQueue ( state ) ; } ) ;
405+ ++ queueSize ;
406+ nextTick ( processQueueFn ( state ) ) ;
332407 }
333408
334409 function Deferred ( ) {
0 commit comments