Green
Green

Reputation: 698

JS uncaught error when Promise finally is used in function

Here is JS code:

function createPromise() {
  const result = new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('my error'));
    }, 2000);
  });
// uncomment this to get error
//  result.finally(() => {
//     console.log('finally before');
//  });

  return result;
}

function test() {
  createPromise()
    .then(() => {
      console.log('promise then');
    })
    .catch((error) => {
      console.log('promise catch:', error);
    })
    .finally(() => {
      console.log('promise finally');
    });
}

Its works properly and after executing I have in the console:

promise catch: Error: my error
promise finall

But after uncommenting:

result.finally(() => {
     console.log('finally before');
  });

In console I have uncaught exception:

finally before
promise catch: Error: my error
promise finally
Uncaught (in promise) Error: my error

Why is this happening and how to explain it?

Upvotes: 4

Views: 1306

Answers (1)

traktor
traktor

Reputation: 19311

The unhandled promise

Like .then and .catch, .finally returns a promise. But unlike .catch or .then with a second argument supplying an onrejection handler, finally does not fulfill the promise it returns if the previous promise in the chain becomes rejected.

So the expected output without the comments for the result promise is the same. But in addition there is the promise returned by .finally in createPromise that doesn't have a rejection handler. Hence the uncaught promise rejection error.

The .finally in function test is okay because the preceding catch clause fulfills the promise it returned so .finally won't reject the promise it returned.


More about the finally method of a promise

  • Generally a .finally clause passes through the result, or promise rejection reason, of the previous promise in a promise chain to the next promise in the chain.

  • However, if finally handler code throws an error or returns a rejected promise when called, the next promise in the chain is rejected for the same error or rejection reason - any data or rejection reason arriving from previous promises in the chain is effectively discarded.


How to stop finally handler code generating uncaught rejection errors.

  1. Return the chain formed by calling finally on the new Promise:

     function createPromise() {
    
         return new Promise((resolve, reject) => {
           setTimeout(() => {
             reject(new Error('my error'));
           }, 2000);
         })
         .finally( () => {
           console.log('finally before');
         });
     }
    

    If the finally handler throws or returns a rejected promise, such thrown value or rejection reason rejects the promise synchronously returned when finally was called, leaving it to the caller of createPromise to handle.

  2. A second method to suppress uncaught rejection reasons is to add a dummy catch handler, although this is rarely used and not recommended in most cases.

     result.finally(() => {
       console.log('finally before');
     })
     .catch( err => err); // avoid unhandled rejection
    

    would handle promise rejection originating from within the finally handler (or promise rejection of result) by having catch return a fulfilled promise that is never used.

Upvotes: 4

Related Questions