Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise .finally gives UnhandledPromiseRejection even if .catch has been used. #28636

Closed
SergeyFromHell opened this issue Jul 11, 2019 · 6 comments
Labels
promises Issues and PRs related to ECMAScript promises.

Comments

@SergeyFromHell
Copy link

SergeyFromHell commented Jul 11, 2019

Version: v12.6.0 (but 10+ also has same behaviour)
Platform: Linux sf2 4.15.0-52-generic #56-Ubuntu SMP Tue Jun 4 22:49:08 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
Subsystem:Promises

If one use promise.finally(..), it leads to unhandled promise rejection warning despite the .catch(..) handler also used.

Example code:
const p = Promise.reject(new Error('BOOM'));
p.catch(() => {});
p.finally(() => {});

Output:
(node:22503) UnhandledPromiseRejectionWarning: Error: BOOM at Object.<anonymous> (/home/user/other/t1/t.js:25:26) at Module._compile (internal/modules/cjs/loader.js:776:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10) at Module.load (internal/modules/cjs/loader.js:643:32) at Function.Module._load (internal/modules/cjs/loader.js:556:12) at Function.Module.runMain (internal/modules/cjs/loader.js:839:10) at internal/main/run_main_module.js:17:11 (node:22503) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 3) (node:22503) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
If you comment last line with .finally handler, no warnings issued.

@Hakerh400
Copy link
Contributor

Hakerh400 commented Jul 11, 2019

That is how promises work. You defined catch and finally handlers on the same promise. The promise then follows both "paths" when resolving/rejecting. One path catches it, but the other contains only finally, making the rejection pass through it, then leading to unhandled rejection. What you probably want is to define finally on the promise returned by the catch call, for example:

const p = Promise.reject(new Error('BOOM'));
p.catch(() => {}).finally(() => {});

Similar question was asked before: nodejs/help#1208

@Fishrock123 Fishrock123 added the promises Issues and PRs related to ECMAScript promises. label Jul 12, 2019
@MuraliSRao
Copy link

Catch is simply to catch the errors.
As a defintion of finally, - the finally block must excute at least once in whatever situation.

@targos
Copy link
Member

targos commented Jul 21, 2019

@Hakerh400's explanation is correct.

@targos targos closed this as completed Jul 21, 2019
@jedwards1211
Copy link

jedwards1211 commented Jun 15, 2021

@targos I just ran into this and it was a really nasty surprise, had me staring at my screen thinking wtf for 30 minutes, even though I've been using promises for years and years. I think the intended behavior could be changed to be more humane.

Is there no way that node could internally flag the promise returned by .finally such that it won't trigger an unhandled rejection like other promises, as long as the finally handler doesn't throw its own error? It would alleviate a lot of suffering.

If there's no catch the original promise would still trigger an unhandled rejection as desired. But unhandled rejections coming from a non-throwing .finally don't provide any concrete benefit that I can imagine. I think they just cause harm.

@targos
Copy link
Member

targos commented Jun 16, 2021

I don't know if Node.js could do something like that, but I find the behavior consistent with the equivalent synchronous code:

try {
  throw new Error('boom')
} finally {
  console.log('hello')
}

This will log "hello" and also leave an unhandled exception.

@jedwards1211
Copy link

jedwards1211 commented Jun 17, 2021

@targos yes I know, but consider this difference -- unlike sync code where you only need to catch an error once, with async code you can end up having to catch the same error multiple times to prevent unhandled rejection, even if only one of those catch handlers actually needs to do anything about it. .finally() can make the same error rear its head after you've already handled it elsewhere.

I realize now, the problem can happen with .then() too. I wish Node would just help me make sure I catch a given error at least once, rather than every place it gets "passed through" to the next link in the promise chain.

Because if I create a dead-end .finally(), I was intending to respond to errors elsewhere, so the error handler for the dead-end is guaranteed to be a no-op.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
promises Issues and PRs related to ECMAScript promises.
Projects
None yet
Development

No branches or pull requests

6 participants