Charlie Schliesser
Charlie Schliesser

Reputation: 8237

Multiple promises in Node with error handling

How can I avoid nesting-hell with a chain of service calls in Node.js, where I'd like to throw a given error and exit the entire chain in certain instances? Here's an example of the chain:

  1. Load Mongoose object.
  2. If load fails, res.send(404); if load succeeds, go to the next then().
  3. Call 3rd party API to get some data.
  4. If API call fails, send proper response (say the proper status code is 500 just for the sake of this problem)
  5. If API call succeeds, render the page.

    SomeMongooseModel.findOne({id:123}).exec()
    .then(function(response)
    {
        // If group is empty, would like to res.send(404) and resolve the 
        // promise immediately.
    })
    .then(function(response)
    {
        // Hit 3rd party API to retrieve data. If there's an issue, return
        // response code of 500 and resolve promise immediately.
    
        // Assuming the API call was a success, build out an object and render
        // the page like so:
        res.render('some.template', {data: some_data});
    });
    

I think this is a decent example of what I'm trying to achieve, but what if we had even more async calls to handle? How can we exit the chain immediately? I've done a bit of searching and I know that I have a lot more to learn, but I'm not finding the ability to simply exit the chains immediately.

Upvotes: 1

Views: 1084

Answers (1)

fmsf
fmsf

Reputation: 37147

When faced with this I normally separate everything into functions, which I then pass a reference into the promise. With good names it also benefits the reading:

function resolveNotFoundIfGroupEmptyOrForwardResponse( response ) { res.send(404) }
function hit3rdPartyApiBasedOnResponse( response ) { 
// throw exception if there is an issue. next step will run the failure state
}    
function render500() { ... }
function renderTemplateWithData( some_data ) { 
   res.render('some.template', {data: some_data});
}

SomeMongooseModel.findOne({id:123}).exec()
    .then( resolveNotFoundIfGroupEmptyOrForwardResponse )
    .then( hit3rdPartyApiBasedOnResponse )
    .then( renderTemplateWithData, render500 )
    .done();

If the function requires an input param that is not comming from the promise chain, then I normally do a function that returns a function.

function doStuffWithParamsCommingFromTwoSides( main_input ) {
   return function( promise_input ) {
         ...
   }
}

then( doStuffWithParamsCommingFromTwoSides( "foobar" ) )

Following Promises/A+ specification, the then steps look like this:

promise.then(onFulfilled, onRejected, onProgress)

Whenever an exception is thrown the next step will run the onRejected. Eventually bubbling up to the done which can also be used to catch the exception bubble.

promise.done(onFulfilled, onRejected, onProgress)

Upvotes: 1

Related Questions