Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Writing Clean Code With Nested Promises

I'm writing an app that talks to Apple to verifyReceipts. They have both a sandbox and production url that you can post to.

When communicating with Apple, if you receive a 21007 status, it means you were posting to the production url, when you should be posting to the sandbox one.

So I wrote some code to facilitate the retry logic. Here's a simplified version of my code:

var request = require('request')
  , Q = require('q')
  ;

var postToService = function(data, url) {
  var deferred = Q.defer();
  var options = {
    data: data,
    url: url
  };

  request.post(options, function(err, response, body) {
    if (err) { 
      deferred.reject(err);
    } else if (hasErrors(response)) {
      deferred.reject(response);
    } else {
      deferred.resolve(body);
    }
  });

  return deferred.promise;
};

exports.verify = function(data) {
  var deferred = Q.defer();

  postToService(data, "https://production-url.com")
    .then(function(body) {
      deferred.resolve(body);
    })
    .fail(function(err) {
      if (err.code === 21007) {
        postToService(data, "https://sandbox-url.com")
          .then(function(body){
            deferred.resolve(body);
          })
          .fail(function(err) {
            deferred.reject(err);
          });
      } else {
        deferred.reject(err);
      }

    });

  return deferred.promise;
};

The retry portion in the verify function is pretty ugly and difficult to read with the nested promises. Is there a better way of doing this?

@jonathanong

This comment has been minimized.

Copy link

jonathanong commented Apr 10, 2013

why even bother using promises for something this simple?

@RobertLowe

This comment has been minimized.

Copy link

RobertLowe commented Apr 10, 2013

Try something like this:

PRODUCTION_URL = "https://production-url.com"
SANDBOX_URL    = "https://sandbox-url.com"

verify = (data, url) ->
  deferred   = Q.defer()
  url      ||= PRODUCTION_URL

  postToService(data, url).then((body) ->
    deferred.resolve body
  ).fail (err) ->
    if err.code is 21007
      verify(data, SANDBOX_URL)
    else
      deferred.reject err

  deferred.promise

You'll have to excuse my coffeescript

Cheers

@jdiamond

This comment has been minimized.

Copy link

jdiamond commented Apr 10, 2013

I think this behaves the same as your version:

function postToService(data, url) {
  return Q.ninvoke(request, 'post', {
    data: data,
    url: url 
  }).then(function(args) {
    var response = args[0];
    var body = args[1];
    if (hasErrors(response)) {
      throw response;
    }
    return body;
  });
}

function verify(data) {
  return postToService(data, 'https://production-url.com')
    .then(null, function(error) {
      if (error.code === 21007) {
        return postToService(data, 'https://sandbox-url.com');
      }
      throw error;
    });
}
@contra

This comment has been minimized.

Copy link

contra commented Apr 10, 2013

I cleaned your code a little https://gist.github.com/Contra/5359196

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.