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

时间:2015-02-27 09:18:33

标签: javascript node.js promise bluebird

短篇小说

  • 谈论承诺/ A + ,拒绝承诺的正确方法是什么 - 抛出错误?但如果我错过了catch - 我的整个应用程序都将会爆炸!

  • 如何使用promisify以及它有什么好处(也许您需要阅读较长版本)?

  • .then(success, fail)真的是anti-pattern我应该始终使用.then(success).catch(error)吗?

更长的版本,被描述为现实生活中的问题(希望有人阅读):

我有一个使用Bluebird(A + Promise实现库)的库,用于从数据库中获取数据(谈论Sequelize)。每个查询都返回一个结果,但有时候它是空的(试图选择一些东西,但没有任何查询)。承诺进入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!
});

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

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(),它能够返回结果(即使出于测试原因),所以有throw new Error如果它失败。但由于我不关心回应,有时我不会添加.catch声明,对吗?但现在 - 如果我不这样做(故意或故障) - 我最终会关闭你的节点应用程序。

我觉得这不是很好。有没有更好的方法来解决它,或者我只需要处理它?<​​/ p>

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

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 +实现)的创建者Petka Antonov撰写的维基文章。它明确地说这是错误

所以我的第二个问题是 - 是吗?如果是 - 为什么?什么是最好的方式?

非常感谢您阅读本文,我希望有人会花时间为我解答:)我应该补充一点,我不想太依赖于框架,所以Sequelize和{{1}只是我最终合作的事情。我的问题是Promises是一个全球性的,而不是这个特定的框架。

2 个答案:

答案 0 :(得分:9)

每个帖子只能询问一个问题: - )

  

.then(success, fail)是否真的是反模式,我应该始终使用.then(success).catch(error)吗?

没有。 They just do different things,但一旦你知道你可以选择合适的那个。

  

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

我认为Bluebird Promisification docs很好地解释了它 - 它已经习惯了convert a callback api来回报承诺。

  

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

是的,抛出错误完全没问题。 then回调中,您可以抛出并自动捕获,从而导致拒绝结果承诺。

您也可以使用return Promise.reject(new Error(…));;两者都会产生完全相同的效果。

  

我的一个朋友帮助了我,我用了一个新的承诺解决问题,像这样:[...]

没有。 You really shouldn't use that。只需使用then并在那里抛出或返回被拒绝的承诺。

  

但是,如果我错过了捕获声明 - 我的整个应用程序将会爆炸!

不,它不会。请注意,.catch()方法不是try catch语句 - 在调用then回调的地方已经发现了错误。然后它只作为参数传递给catch回调,不需要.catch()调用来捕获异常。

当你错过.catch()时,你的应用不会被击中。所有会发生的事情是,您有一个被拒绝的承诺,您的应用程序的其余部分将不会受此影响。您将获得unhandled rejection event,可以是dealt with globally

当然,应该不会发生;每个承诺每个承诺实例)都应该以适当处理错误的.catch()结束。但是你肯定不需要在每个辅助函数中都有.catch(),当它向其他地方返回一个promise时,那么调用者通常会处理错误。

答案 1 :(得分:6)

  

谈论Promises / A +,拒绝承诺的正确方法是什么 - 抛出错误?但如果我错过了这个问题 - 我的整个应用程序都将会爆炸!

如果您使用返回代码来表示错误而不是异常,那么错过检查将导致细微的错误和不稳定的行为。忘记处理错误是一个错误,但让你的应用程序在这样一个不幸的情况下爆炸是more often better而不是让它继续处于损坏的状态。

调试忘记的错误代码检查只是由于应用程序在一些无关的错误源位置上表现得很奇怪,很容易比调试一个被遗忘的catch更昂贵几个数量级,因为你有错误信息,错误源和堆栈跟踪。因此,如果您希望在典型的应用程序开发方案中获得生产力,则异常会以相当大的优势获胜。

  

。然后(成功,失败)

这通常表示您使用promises作为美化回调,其中回调只是单独传递。这显然是一种反模式,因为以这种方式使用promises不会带来任何好处。如果情况并非如此,即使这样,与使用.catch()相比,您只是使代码的可读性稍差,类似于method(true, true, false)的可读性远低于method({option1: true, option2: true, option3: false})

  

如何使用promisify以及它有什么好处(也许你需要阅读更长版本)?

大多数模块只公开一个回调接口,promisify将一个回调接口自动转换为一个promise接口。如果您手动编写此类代码(使用延迟或new Promise),则会浪费您的时间。

  

所以我的第二个问题是 - 是吗?如果是 - 为什么?什么是最好的方式?

最好的方法是结合承诺:

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

它更好,因为它更简单,更短,更易读,而且更不容易出错。完全相同的事情。