Should a Promise.reject message be wrapped in Error?
Yes, it most definitely should. A string is not an error, when you have errors usually it means something went wrong which means you'd really enjoy a good stack trace. No error - no stack trace.
Just like with try/catch, if you add .catch
to a thrown rejection, you want to be able to log the stack trace, throwing strings ruins that for you.
I'm on mobile so this answer is rather short but I really can't emphasize enough how important this is. In large (10K+ LoC) apps stack traces in rejections really made the difference between easy remote bug hunting and a long night in the office.
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). Should Promise be rejected with Error or string?
A look at Mozilla's documentation is helpful: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
Promise.reject("Testing static reject").then(function(reason) {
// not called
}, function(reason) {
console.log(reason); // "Testing static reject"
});
Promise.reject(new Error("fail")).then(function(error) {
// not called
}, function(error) {
console.log(error); // Stacktrace
});
It shows both strings and errors as valid "reasons" for a rejection. The main thing (I think) is that the "reason" should be meaningful.If a stack trace is helpful then it may be better to provide an error. If a simple string is all that's needed, then it will suffice.
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
});
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
.
Nodejs Promise reject wont forward error messages
You have a slight misunderstanding of what's going on here. Calling reject
will reject the promise, and the subsequent resolve
will not resolve it. So far so good. The problem is that it won't stop execution without a return
. So you're essentially running unnecessary code which happens to cause an error.
You should ensure that the resolve
code is not executed in the case of an error. You can accomplish this with return reject
or by wrapping the resolve
in an else
block.
Keep in mind there's nothing special about resolve
/reject
here. They're simple function calls and function calls do nothing to stop execution.
Promise reject() causes Uncaught (in promise) warning
This happens because you do not attach a catch handler to the promise returned by the first then
method, which therefore is without handler for when the promise rejects. You do have one for the promise p
in the last line, but not for the chained promise, returned by the then
method, in the line before it.
As you correctly added in comments below, when a catch handler is not provided (or it's not a function), the default one will throw the error. Within a promise chain this error can be caught down the line with a catch
method callback, but if none is there, the JavaScript engine will deal with the error like with any other uncaught error, and apply the default handler in such circumstances, which results in the output you see in the console.
To avoid this, chain the .catch
method to the promise returned by the first then
, like this:
p.then( result => console.log('Fulfilled'))
.catch( error => console.log(error) );
What happens if you don't resolve or reject a promise?
A promise is just an object with properties in Javascript. There's no magic to it. So failing to resolve or reject a promise just fails to ever change the state from "pending" to anything else. This doesn't cause any fundamental problem in Javascript because a promise is just a regular Javascript object. The promise will still get garbage collected (even if still pending) if no code keeps a reference to the promise.
The real consequence here is what does that mean to the consumer of the promise if its state is never changed? Any .then()
or .catch()
listeners for resolve or reject transitions will never get called. Most code that uses promises expects them to resolve or reject at some point in the future (that's why promises are used in the first place). If they don't, then that code generally never gets to finish its work.
It's possible that you could have some other code that finishes the work for that task and the promise is just abandoned without ever doing its thing. There's no internal problem in Javascript if you do it that way, but it is not how promises were designed to work and is generally not how the consumer of promises expect them to work.
In this particular case, it's all OK and a redirect is a somewhat special and unique case. A redirect to a new browser page will completely clear the current page state (including all Javascript state) so it's perfectly fine to take a shortcut with the redirect and just leave other things unresolved. The system will completely reinitialize your Javascript state when the new page starts to load so any promises that were still pending will get cleaned up.As you can see if the status is 401, I am redirecting to login page.
Promise is neither resolved nor rejected.Is this code OK? Or is there any better way to accomplish this.
Related Topics
Angularjs Browser Autofill Workaround by Using a Directive
Attaching Click Event to a Jquery Object Not Yet Added to the Dom
What Are Some Empirical Technical Reasons Not to Use Jquery
JavaScript Time Zone Is Wrong for Past Daylight Saving Time Transition Rules
JavaScript Settimeout and Loops
Why Is There a 'Null' Value in JavaScript
Cross-Browser Bookmark/Add to Favorites JavaScript
Printing a Web Page Using Just Url and Without Opening New Window
Conversion Between Utf-8 Arraybuffer and String
JavaScript Array Concat Not Working. Why
How to Stop Events Bubbling in Jquery
Remove Whitespace and Line Breaks Between HTML Elements Using Jquery
Regular Expression to Match Exactly 5 Digits
Backbone: Why Assign '$('#Footer')' to 'El'