5

I do not want to type .catch for every promise I use. Without doing this errors caused by promises are supremely unhelpful.

Using an entire library like bluebird purely for this purpose makes me uncomfortable.

Mentor
  • 965
  • 9
  • 21
  • What do you mean by "every promise you use"? You only need to `catch` errors at the end of the chain. Also, what do you mean by "swallowing"? If you want to prevent that, what should happen with the errors instead? – Bergi Jan 11 '17 at 02:14
  • When using a lot of promises putting .catch after every chain can be exhausting and not at all DRY. Edit: With swallowing I mean that no error lines or details are provided. The errors instead should by default have a handler associated, in my case I wanted them to show up in my node console. – Mentor Jan 11 '17 at 13:11
  • 1
    There are other ways to make such code dry than to omit `.catch(function(e) {…})` that is the same everywhere. Where are your many chains started? Event handlers, route handlers? Then define a wrapper function that installs the handler and treats the returned promises appropriately. If you could post your code, I'd be able to write a more specific answer. – Bergi Jan 11 '17 at 15:03
  • 1
    Writing code without any `.catch()` handlers is simply just bad code. You don't globally diagnose missing reject handlers. It's just not how you write good code. You don't have to put a `.catch()` on every promise. You should have one on every promise chain though as I explain the logic for in my answer. If you're the one who downvoted my answer, then I apologize for giving you the proper programming answer to your situation. You can't do it the way you're asking so I've described how you should handle rejections. If you don't like that, then I can delete my answer. – jfriend00 Jan 11 '17 at 18:36
  • 1
    @jfriend00, didn't notice you commented there. I did not downvote you, but disagree that it is bad code. My purpose was simply to have a default error handler and in that prevent a lot of identical .catches. Where errors are relevant to program function or user experience I have .catches handle the error. In some cases however I simply want the error logged (either to a console, log file or error db). There is nothing bad about this :) – Mentor Aug 28 '17 at 10:30
  • @Mentor - Show us some actual code and we can discuss this on a more specific and factual basis. For a robust server, I can think of NO reason why you should ever have a promise that can reject without a `.catch()` somewhere in its own promise chain - you just get silent errors. It can be at a high level in the chain, but somewhere in the promise chain. In fact, it is going to become an error in future versions of Javascript to even have a promise chain without a `.catch()`. In some promise libraries, it is already a warning. estus' answer summarizes it. – jfriend00 Aug 28 '17 at 15:08
  • @jfriend00 this method was meant for development purposes. Besides, the reason for my question was exactly what you say, the prevention of silent errors. By having a handler on 'unhandledRejection' you can display/log the error stack and easily debug. I have no intention to do this in production. – Mentor Aug 31 '17 at 10:12

2 Answers2

6

For error tracking during development, V8 (recent Node.js and Chrome versions) already has got unhandledRejection (Node.js) and unhandledrejection (Chrome) event listener by default, which results in UnhandledPromiseRejectionWarning warning in Node.js and Uncaught (in promise) error in Chrome.

Deprecation warning in Node 7 states that this will be changed in future Node.js versions:

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.

Error handling in promises is not optional and should be treated on par with try...catch. Every promise that has a chance to be rejected should be chained with catch. And in the case of async...await or co, .catch is identical to try...catch.

If the error is supposed to be ignored, it has to be explicitly caught. If error handling needs to be consistent across the app and comply to log levels, this may be provided by design. E.g. with conventional debug package:

const debug = require('debug');

function catchFn(err, ns = 'myapp:caughtPromises') {
    debug(ns)(err);
}

function catchPromise(promise, ns) {
    promise.catch(err => catchFn(err, ns));
    return promise;
}

...

try {
  await promise;
} catch (err) {
  catchFn(err);
}

// or
catchPromise(promise);

// or
promise.catch(catchFn);

catchFn can be extended to use third-party logging service as well.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • 2
    This doesn't appear to be what the OP is asking about. I think the OP has a `.catch()` at a higher level so the rejection is not unhandled. It appears to me that they are complaining about how hard it is to tell where the original rejection was without using stack traces in something like Bluebird. – jfriend00 Jan 10 '17 at 20:54
  • @jfriend00 The fact that the OP posted the answer with `unhandledRejection` listener speaks for itself. Promises don't seem to have `catch` at higher level, because a listener would not be applicable in this case. If the question is about proper stack traces in the first place, it was not worded correctly (and in this case I would suggest to just stick to BB and don't reinvent the wheel, especially for Node). – Estus Flask Jan 10 '17 at 22:38
  • I use node v7, and indeed there is no default handler. That is why I posted this. It was bothering me that promises without catch do show errors, but no line numbers and reasons. As for "Every promise that has a chance to be rejected should be chained with catch": Yes, if there is no default handler indeed. But in larger projects this means a lot of needless code. – Mentor Jan 11 '17 at 13:12
  • Unless Node runs with `--no-warnings` flag, `Promise.reject();` will produce `UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): undefined` warning in both Node 6 and 7. I'm not sure if there are other conditions that may suppress warning output. Omitting `catch`. is the same thing as omitting `try...catch. This is also what deprecation message implies - it will throw an exception in next Node. Sure, you can omit good practices and catch all exceptions globally, but suggesting this approach in the answer is just wrong. – Estus Flask Jan 11 '17 at 16:55
1

Add a handler to your process for unhandled rejections. You can handle them once like that instead of copy-pasting .catch statements all the time.

process.on( 'unhandledRejection', ( error, promise ) => {
    console.log( 'UPR: ' + promise + ' with ' + error )
    console.log( error.stack )
} )
Mentor
  • 965
  • 9
  • 21
  • catch() should be used to _handle_ errors, not just report them. After initial development efforts, we usually want to know when and where a problem occurred; the more context the better, so use macros or a named handler; `.catch(fnErr)` doesn't take much effort to type... – dandavis Jan 10 '17 at 13:01
  • Using unhandledRejection for promises is not god idea because unhandledRegjection event can fire somewhere else. – Vishnu Mishra Jan 10 '17 at 18:20
  • It is a bad practice to save time on error handling and handle errors globally. But `unhandledRejection` can be a reasonable way to track unhandled rejections that weren't caught by oversight. But here's the problem. `error` is a rejection in fact, not necessarily an instance of `Error`. While it is a good habit to `throw new Error` if a rejection means error, `error` can be anything and miss `stack` property. So here we are - an error that weren't handled by careless dev in time comes out of the blue with no call stack (and also throws an exception because `error` is not an object). – Estus Flask Jan 11 '17 at 16:13
  • @danavis, in this case the stack gives me all the context needed. And indeed this technique was meant for during development, not live applications (that would be lazy and unfortunate). – Mentor Aug 28 '17 at 10:32
  • @estus this code is meant to catch errors during development that need no specific error handling. You're 100% right that this is only useful for unhandled rejections but production code should never hit this. – Mentor Aug 28 '17 at 10:34