JavaScript Promises - Reject VS. Throw

JavaScript Promises - reject vs. throw

There is no advantage of using one vs the other, but, there is a specific case where throw won't work. However, those cases can be fixed.

Any time you are inside of a promise callback, you can use throw. However, if you're in any other asynchronous callback, you must use reject.

For example, this won't trigger the catch:

new Promise(function() {
setTimeout(function() {
throw 'or nah';
// return Promise.reject('or nah'); also won't work
}, 1000);
}).catch(function(e) {
console.log(e); // doesn't happen
});

Should I throw an error or return a rejected promise inside an async function?

They are correct.

The call to myCustomFunction assumes that a promise is returned at all times (.then and .catch deal with resolved and rejected promises, respectively). When you throw an error, the function doesn't return a promise.

You could use this to catch the error:

try {
myModule.myCustomFunction(someInput).then(result => {
// carry on
})
.catch(err => {
// do something with the error
})
} catch(err) {
...
}

But as you can see, this results in two error handlers: try/catch for the synchronously thrown error, and .catch for any rejected promises that sns.createTopic(someParams) may return.

That's why it's better to use Promise.reject():

module.exports.myCustomFunction = input => {

if (badInput) {
return Promise.reject('failed');
}

return sns.createTopic(someParams).promise()
}

Then, the .catch will catch both types of errors/rejections.

NB: for newer versions of Node.js (v7.6 and up, I believe), the following will also work:

module.exports.myCustomFunction = async input => {

if (badInput) {
throw new Error('failed');
}

return sns.createTopic(someParams).promise()
}

The key here is the async keyword. By using this keyword, the function results are wrapped by a promise automatically (similar to what @peteb's answer is showing).

await Promise.reject or throw error to bail out?

It's semantically correct to use throw in promise control flow, this is generally preferable way to bail out of promise chain.

Depending on coding style, await Promise.reject(...) may be used to differentiate between real errors and expected rejections. Rejected promise with string reason is valid but throw 'invalid result' is considered style problem that may be addressed with linter rules, because it's conventional to use Error instances as exceptions.

The reason why it's important is because string exceptions can't be detected with instanceof Error and don't have message property, consistent error logging as console.warn(error.message) will result in obscure undefined entries.

// ok
class Success extends Error {}
try {
throw new Success('just a friendly notification');
} catch (err) {
if (!(err instanceof Success)) {
console.warn(err.message);
throw err;
}
}

// more or less
const SUCCESS = 'just a friendly notification';
try {
await Promise.reject(SUCCESS);
} catch (err) {
if (err !== SUCCESS)) {
console.warn(err.message);
throw err;
}
}

// not ok
try {
throw 'exception';
} catch (err) {
if (typeof err === 'string') {
console.warn(err);
} else {
console.warn(err.message);
}

throw err;
}

Since invalid result is actually an error, it's reasonable to make it one:

  throw new TypeError('invalid result');

I am not talking about the promise chain(the whole point of my question), so I don't think the thread JavaScript Promises - reject vs. throw answered my question.

async function is syntactic sugar for promise chain, so all points that are applicable to promises are applicable to async as well.

There may be cases when throwing an error is not the same as rejecting a promise, but they are specific to other promise implementations like AngularJS $q and don't affect ES6 promises. Synchronous error in Promise constructor results in exception, this also isn't applicable to async.

Promise constructor with reject call vs throwing error

Is there any difference between using reject (in p2) from the Promise api, and throwing an error (in p1) using throw?

Yes, you cannot use throw asynchronously, while reject is a callback. For example, some timeout:

new Promise(_, reject) {
setTimeout(reject, 1000);
});

Its exactly the same?

No, at least not when other code follows your statement. throw immediately completes the resolver function, while calling reject continues execution normally - after having "marked" the promise as rejected.

Also, engines might provide different exception debugging information if you throw error objects.

For your specific example, you are right that p1 and p2 are indistinguishable from the outside.

JavaScript Promise: Reject handler vs catch

The difference is that if an error occurs inside resolveHandler it won't be handled by the rejectHandler, that one only handles rejections in the original promise.

The rejectHandler is not used in combination with catch that much, because most of the time we only care about that something went wrong.

Creating only one errorhandler makes the code easier to reason about.

If a specific promise in the chain should handled differently that can be a reason to use a rejectHandler, but i'd probably write a catch().then().catch() in that case.

Best practice: Promises reject/throw

Obviously, if there's async code involved (such as an database or HTTP request), you can't use throw, since the stack has changed.

That's half the point of promises. Promises are throw safe, they let you use sync facilities like return and throw in asynchronous code.

Synchronous:

try {
return fn();
} catch (e) {
// handle error
return recover(e);
}

Promises:

fn().catch(recover);

Or more verbosely:

Promise.resolve().then(function() {
return fn();
}).catch(function(e) {
return recover(e);
});


Related Topics



Leave a reply



Submit