2013-04-09 90 views
5

我正在写一个应用程序与Apple进行验证以验证接收。他们有一个沙箱和生产网址,你可以发布。使用嵌套承诺编写干净的代码

与Apple通信时,如果您收到21007状态,这意味着您要发布到生产网址,而您应该发布到沙箱上。

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

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

在验证功能的重试部分是相当丑陋,很难与嵌套的承诺阅读。有没有更好的方法来做到这一点?

+0

一个想法是利用冰的CoffeeScript这对于延续传递(类似于C#的异步/等待)语法支持:http://maxtaco.github.io/coffee-script /。不幸的是,这将需要使用CoffeeScript,以及不受grunt等支持的“非标准”变体。 – millimoose 2013-04-09 22:09:39

回答

5

您可以拒绝处理重新抛出异常,继续拒绝承诺,或者你可以返回一个新的承诺,以取代拒绝。

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 
     } 
    }); 
}; 
+0

我喜欢这个。很好,干净而简单! – Anton 2013-04-10 00:49:46

0

您可能会考虑类似以下内容。我认为明智地使用空格可以提高可读性。你可能会想找到一个合理的风格标准,你的团队感觉很好,并坚持下去!

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

这里有几种可能性。因为这个问题具有个人品味的因素,所以你可能会也可能不会喜欢你所看到的!

入场 - 我没有测试此代码

选项1 - 使用的包装为resolvereject。这会以助手功能的形式增加“噪音”,但会整理其余部分。

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 - 使用bind和'方法句柄'(#2上的细微变化)。

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

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

    return deferred.promise; 
}; 

选项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; 
}; 
0

斯图亚特的答案是对的,关键是链接承诺。我想澄清一点,不需要使用Q.defer来包装。它甚至被认为是反模式。看到这里的原因The Deferred anti-pattern想到的

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

你可以完全避免使用'Q.ninvoke'(和配偶) – Bergi 2015-04-07 17:57:17