Javascript 使用嵌套承诺编写干净的代码

Javascript 使用嵌套承诺编写干净的代码,javascript,node.js,promise,Javascript,Node.js,Promise,我正在编写一个与苹果对话的应用程序来验证收据。他们有一个沙箱和一个你可以发布到的产品url 在与苹果沟通时,如果您收到21007状态,则表示您正在发布到生产url,而您应该发布到沙盒url 所以我写了一些代码来简化重试逻辑。以下是我的代码的简化版本: var request = require('request') , Q = require('q') ; var postToService = function(data, url) { var deferred = Q.defe

我正在编写一个与苹果对话的应用程序来验证收据。他们有一个沙箱和一个你可以发布到的产品url

在与苹果沟通时,如果您收到21007状态,则表示您正在发布到生产url,而您应该发布到沙盒url

所以我写了一些代码来简化重试逻辑。以下是我的代码的简化版本:

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

verify函数中的重试部分非常难看,很难用嵌套的承诺来读取。有更好的方法吗?

< P>你可以考虑如下的事情。我认为明智地使用空格有助于提高可读性。你可能会想找到一个合理的风格标准,让你的团队感觉良好,并坚持下去

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

  postToService(data, "https://production-url.com")

    .then(deferred.resolve, function(err) {

      if (err.code === 21007) {

        postToService(data, "https://sandbox-url.com")

          .then(deferred.resolve, deferred.reject);

      } else { deferred.reject(err); }

    });

 return deferred.promise;
};

这里有几个可能性。因为这个问题有个人品味,你可能喜欢也可能不喜欢你看到的东西

(承认-我没有测试过此代码)

选项1-为
解析
拒绝
使用包装器。这会以辅助函数的形式添加“噪波”,但会整理其余的

var resolve = function (deferred, ob) {
  return function () {
    deferred.resolve(ob);
  };
};

var reject = function (deferred, ob) {
  return function () {
    deferred.reject(ob);
  };
};

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

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

  return deferred.promise;
};
选项2-使用绑定。这有使用现有JS功能的优点,但是在创建回调时,您有对
deferred
的重复引用

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

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

  return deferred.promise;
};
选项3-使用绑定和“方法句柄”(对#2的微小变化)

选项4-猴子补丁推迟

function patch(deferred) {
  deferred.resolveFn = function (ob) {
    return function () {
      deferred.resolve(ob);
    };
  };
  deferred.rejectFn = function (ob) {
    return function () {
      deferred.reject(ob);
    };
  };
  return deferred;
}

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

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

  return deferred.promise;
};

您可以在拒绝处理程序中重新抛出错误以继续拒绝承诺,也可以返回新承诺以替换拒绝

exports.verify = function(data) {
  return postToService(data, "https://production-url.com")
    .fail(function(err) {
      if (err.code === 21007) {
        return postToService(data, "https://sandbox-url.com")
      } else {
        throw err
      }
    });
};

斯图尔特的回答是对的,关键是要兑现承诺。我想澄清的是,仅仅为了包装,不需要使用Q.defer。这甚至被认为是一种反模式。看这里的原因


我想到的一个想法是使用冰镇咖啡脚本,它在语法上支持继续传递(类似于C#的async/await):。不幸的是,这需要使用CoffeeScript,以及grunt等不支持的“非标准”变体。我喜欢这样。漂亮、干净、简单!使用
Q.ninvoke
(和配偶)可以完全避免延迟
exports.verify = function(data) {
  return postToService(data, "https://production-url.com")
    .fail(function(err) {
      if (err.code === 21007) {
        return postToService(data, "https://sandbox-url.com")
      } else {
        throw err
      }
    });
};
var request = require('request')
    , Q = require('q');

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


export.verify = function() {

  return postToProduction(data)
         .fail( function(error) {
             if (error.code === 21007 ) return postToSanbox(data);
             throw error;
         });
}

function postToProduction(data) {
    return postToService(data, PRODUCTION_URL);
}

function postToSandbox(data) {
    return postToService(data, SANDBOX_URL);
}

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

   var options = {
      data: data,
      url: url
   };

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

    deferred.resolve(body);    
  });

  return deferred.promise;   
}