I am thinking about when exactly I need to reject a promise. I found a couple of questions regarding this topic, but could not find a proper answer. When should I reject a promise?
This article http://howtonode.org/6666a4b74d7434144cff717c828be2c3953d46e7/promises says:
Is this the principle guideline? That one only reject a promise if an exception occured?
But in case of a function like
findUserByEmail()
I'd would expect the function to return a user, so that I can continue the chain without verifying the result
findUserByEmail()
.then(sendWelcomeBackEmail)
.then(doSomeNiceStuff)
.then(etc..)
What are best / common practises?
In general you can think of rejecting as being analogous to a synchronous throw
and fulfilling as being analogous to a synchronous return
. You should reject whenever the function is unsuccessful in some way. That could be a timeout, a network error, incorrect input etc. etc.
Rejecting a promise, just like throwing an exception, is useful for control flow. It doesn't have to represent a truly unexpected error; it can represent a problem that you fully anticipate and handle:
function getProfile(email) {
return getProfileOverNetwork(email)
.then(null, function (err) {
//something went wrong getting the profile
if (err.code === 'NonExistantUser') {
return defaultUser;
} else if (profileCached(email)) {
return getProfileFromCache(email);//fall back to cached profile
} else {
throw err;//sometimes we don't have a nice way of handling it
}
})
}
The rejection lets us jump over the normal success behavior until we get to a method that knows how to handle it. As another example, we might have some function that's deeply nested at the bottom of the applications stack, which rejects. This might not be handled until the very top of the stack, where we could log it. The point is that rejections travel up the stack just like exceptions do in synchronous code.
In general, wherever possible, if you are struggling to write some asynchronous code, you should think "what would I write if this were synchronous". It's usually a fairly simple transformation to get from that, to the promised equivalent.
A nice example of where rejected promises might be used is in an exists
method:
function exists(filePath) {
return stat(filePath) //where stat gets last updated time etc. of the file
.then(function () { return true; }, function () { return false; })
}
Notice how in this case, the rejection is totally expected and just means the file does not exist. Notice also how it parallels the synchronous function though:
function existsSync(filePath) {
try {
statSync(filePath);
return true;
} catch (ex) {
return false;
}
}
Returning to your example:
I would normally chose to reject the promise resulting from findUserByEmail
if no user was found. It's something you fully expect to happen sometimes, but it's the exception from the norm, and should probably be handled pretty similarly to all other errors. Similarly if I were writing a synchronous function I would have it throw
an exception.
Sometimes it might be useful to just return null
instead, but this would depend on your applications logic and is probably not the best way to go.