When to reject/resolve a promise When to reject/resolve a promise node.js node.js

When to reject/resolve a promise


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;  }}

Your Example

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.


I know where you're coming from. Q and Q documentation can quite easily have you believe that deferred/promise rejection is all about exception handling.

This is not necessarily the case.

A deferred can be rejected for whatever reason your application requires.

Deferreds/promises are all about handling responses from asynchronous processes, and every asynchronous processes can result in a variety of outcomes - some of which are "successful" and some "unsuccessful". You may choose to reject your deferred - for whatever reason, regardless of whether the outcome was nominally successful or unsuccessful, and without an exception ever having been thrown, either in javascript or in the asynchronous process.

You may also choose to implement a timeout on an asynchronous process, in which case you might choose to reject a deferred without a response (successful or unsuccessful) having been received. In fact, for timeouts, this is what you would typically choose to do.