如何拒绝(和正确使用)承诺?

小故事

  • 谈论Promises / A + ,拒绝承诺的正确方法是什么 – 抛出一个错误? 但是,如果我错过了 – 我的整个应用程序将打击!

  • 如何使用promisify和它的好处(也许你需要阅读更长的版本)?

  • .then(success, fail)真的是反模式 ,我应该总是使用。 .then(success).catch(error)

更长的版本 ,被描述为现实生活中的问题(会爱上某人阅读):

我有一个使用Bluebird (A + Promise实现库)的库,从数据库中提取数据(谈论Sequelize )。 每个查询返回一个结果,但有时它是空的(试图select一些东西,但没有)。 承诺进入result函数,因为没有错误的原因(没有结果不是错误)。 例:

 Entity.find(1).then(function(result) { // always ending here, despite result is {} }).catch(function(error) { // never ends here }); 

我想包装这个,并检查结果是否为空(不能检查我的代码中的任何地方)。 我做到了这一点:

 function findFirst() { return Entity.find(1).then(function(result) { if (result) { // add proper checks if needed return result; // will invoke the success function } else { // I WANT TO INVOKE THE ERROR, HOW?! :) } }).catch(function(error) { // never ends here }); } findFirst().then(function(result) { // I HAVE a result }).catch(function(error) { // ERROR function - there's either sql error OR there is no result! }); 

如果你还在我身边 – 我希望你能理解到底是怎么回事。 我想以某种方式启动错误function(其中“错误function”是)。 事情是 – 我不知道如何。 我试过这些东西:

 this.reject('reason'); // doesn't work, this is not a promise, Sequelize related return new Error('reason'); // calls success function, with error argument return false; // calls success function with false argument throw new Error('reason'); // works, but if .catch is missing => BLOW! 

正如你可以看到我的意见(和规格),抛出一个错误运作良好。 但是,有一个很大的, 如果我错过了.catch声明,我的整个应用程序爆炸。

为什么我不想要这个? 比方说,我想在我的数据库中增加一个计数器。 我不关心结果 – 我只是做HTTP请求。所以我可以调用incrementInDB() ,它有能力返回结果(即使出于testing的原因),所以如果失败了,就会throw new Error 。 但是由于我不在乎反应, 有时候我不会添加.catch语句 ,对吗? 但是现在 – 如果我没有(故意或者错误) – 我最终放弃了你的节点应用程序。

我不觉得这很好 。 有没有更好的方法来解决它,或者我只需要处理它呢?

我的一个朋友帮助我,我用一个新的承诺来解决这个问题,就像这样:

 function findFirst() { var deferred = new Promise.pending(); // doesnt' matter if it's Bluebird or Q, just defer Entity.find(1).then(function(result) { if (result) { // add proper checks if needed deferred.resolve(result); } else { deferred.reject('no result'); } }).catch(function(error) { deferred.reject('mysql error'); ); return deferred.promise; // return a promise, no matter of framework } 

奇迹般有效! 我进入了这个: Promise Anti Patterns – 由Bluebird(A +实现)的创build者Petka Antonov编写的wiki文章。 明确地说这是错误的

所以我的第二个问题是 – 是这样吗? 如果是 – 为什么? 什么是最好的方法?

非常感谢读这个,我希望有人会花时间来回答我:)我应该补充说,我不想过多依赖框架,所以SequelizeBluebird只是我最终合作的东西。 我的问题是Promise作为一个全球性的,而不是这个特定的框架。

请每个post询问一个问题:-)

.then(success, fail)真的是反模式,我应该总是使用。 .then(success).catch(error)

不, 他们只是做不同的事情 ,但是一旦你知道可以select合适的事情。

如何使用promisify和它有什么好处?

我认为蓝鸟Promisification文档解释得非常好 – 它被用来将callbackapi转换为返回承诺的api 。

谈论Promises / A +,拒绝承诺的正确方法是什么 – 抛出一个错误?

是的,抛出一个错误是完全正确的。 在一个callback内部 ,你可以抛出并自动捕获,导致拒绝结果的承诺。

你也可以使用return Promise.reject (new Error(…)); ; 两者将有绝对相同的效果。

我的一个朋友帮助我,我用一个新的承诺来解决这个问题,比如:[…]

不, 你真的不应该用这个 。 只要使用, then抛出或返回一个被拒绝的承诺在那里。

但是,如果我错过了发表声明 – 我的整个应用程序将打击!

不,不会的 注意.catch()方法不是一个try catch语句 – 错误将在您的callback被调用的地方被捕获。 然后它只作为parameter passing给catchcallback函数, .catch()调用不需要捕获exception。

而当你错过.catch() ,你的应用程序不会吹。 所有发生的事情是,你有一个被拒绝的承诺,其余的应用程序不会受到这个影响。 你会得到一个未处理的拒绝事件 ,可以在全球范围内处理 。

当然,这不应该发生; 每一个承诺不是每个承诺实例 )都应该以一个.catch()来处理错误。 但是你绝对不需要每个帮助函数中的.catch() ,当它将一个promise传递给其他地方的时候,那么错误通常会被调用者处理。

谈论Promises / A +,拒绝承诺的正确方法是什么 – 抛出一个错误? 但是,如果我错过了 – 我的整个应用程序将打击!

如果您使用返回码来表示错误而不是exception,则错过检查将会导致细微的错误和不稳定的行为。 忘记处理错误是一个错误,但是在这样一个不幸的情况下让你的应用程序崩溃通常比让它继续损坏的状态更好 。

debugging被遗忘的错误代码检查,只有在某个与错误源不相关的地方,应用程序的行为exception显而易见的情况下,debugging被遗忘的错误的代码比debugging被遗忘的错误要容易很多个数量级,因为你有错误信息,错误和堆栈跟踪。 所以如果你想在典型的应用程序开发场景中获得生产力,那么exception就会有相当大的余地。

那么(成功,失败)

这通常表示您正在使用Promise作为荣耀的callback,其中callback只是单独传递。 这显然是一种反模式,因为你不会以这种方式使用promise。 如果情况并非如此,即使这样,与使用.catch() method(true, true, false)相比,您的代码的可读性.catch() ,类似于method(true, true, false)可读性比method({option1: true, option2: true, option3: false})

如何使用promisify和它的好处(也许你需要阅读更长的版本)?

大多数模块只公开一个callback接口,promisify自动将一个callback接口变成一个promise接口。 如果你手动编写这样的代码(使用延迟或new Promise ),你正在浪费你的时间。

所以我的第二个问题是 – 是这样吗? 如果是 – 为什么? 什么是最好的方法?

最好的办法是把这个承诺链接起来:

 function findFirst() { return Entity.find(1).tap(function(result) { if (!result) throw new Error("no result"); }); } 

这样做更好,因为它更简单,更短,更具可读性,而且更容易出错。