了解明确的承诺构建反模式

时间:2018-12-24 07:02:33

标签: javascript node.js mongoose promise

在我的CertainPerformance中突出显示的

previous post建议我避免参考following question in stackoverflow

的显式Promise构造反模式

坦白说,我是JS和node的新手,并且我并没有经常使用promise。我去看了那些文章,但要么我无法理解或无法联系,要么我对诺言的理解一直含糊不清

因此,我决定在一个新线程中询问此问题并寻求帮助。

那么我在做什么,为什么要这么做

我正在创建帮助程序/通用函数,可以用来使代码保持整洁,如果万一我想随时更改函数内部的任何内容,则不必手动更改每个函数。

这些是我已经完成的功能

//Find user by email Address 
const findUserByEmail = (emailAddress) => {
    return new Promise((resolve, reject) => {
     User.findOne({email: emailAddress}).then(response => {
        resolve(res)
      }).catch(error => {
        reject("Error in findUserByEmail", error);
      })
    })
}

//Create User 
const createNewUser = (newUserDetails) => {
    return new Promise((resolve, reject) => {
      new User({
         fullName: newUserDetails.fullName,
         email: newUserDetails.email,
         image: newUserDetails.image,
         gender: newUserDetails.gender,
         age: newUserDetails.age
      }).save().then((response) => {
          resolve(response)
      }).catch((error) => {
          reject("Problem in Creating New User", error)
      })
    })
}

问题1

现在,我假设SomePerformance表示对诺言的过度使用,因为当我已经在猫鼬return new Promise((resolve, reject) => {中使用诺言时,我正在创建新的诺言User.findOne({email: emailAddress}).then(response => {吗?

但是我创建这些承诺的原因是,当我在导入后从应用程序中的任何位置调用这些帮助程序功能

const { findUserByEmail } = require("./my_db_query");

我可能希望它返回响应或在出现错误的情况下抛出错误

findUserByEmail("test@example.com").then(/*...*/).catch(/*...*/);

如果我在不添加新承诺的情况下更改了上述代码段

 function findUserByEmail  (email) {
       return User.findOne({email: email}).then(currentUser => currentUser).catch(error => error)
    } 

问题2

然后我可能无法在.then中的.catchfindUserByEmail("test@example.com")

在App的API路由中,我将调用findUserByEmail("test@example.com")函数,如果发生错误,我想做其他事情(不同情况下会有所不同,因此我无法在其中使用它我的助手功能)。

问题3

是的,现在做return new Promise((resolve, reject) => {而不是只做一个return User.findOne(还是有意义的吗?

2 个答案:

答案 0 :(得分:4)

由于.findOne已经返回了Promise,因此无需使用new Promise来构造一个新的-只需链接到现有的{{1 }}根据需要与Promise.then链接。这样的.catch链可以有任意数量的Promise.then-仅仅是因为您消耗了.catch和一个Promise不会阻止您在其他地方使用相同的解析值。为了说明:

.then

换句话说-每次您想使用另一个makePromise() .then((result) => { console.log(result); // Returning inside a `.then` will pass along the value to the next `.then`: return result; }) .then((result) => { // this `result` will be the same as the one above }); 时都不需要构造一个new Promise。所以:

  

然后我可能无法在findUserByEmail(“ test@example.com”)中然后.catch

是不正确的-您确实可以根据需要链接到具有多个.then.then的现有Promise的末尾。

请注意,仅返回其参数并且不执行其他任何操作(例如.catch)的.then是多余的-它什么也不做。另外请注意,.then(currentUser => currentUser)会捕获承诺拒绝并解析为已解决 .catch。所以如果你这样做

Promise

function findUserByEmail(email) { return User.findOne({email: email}) .then(currentUser => currentUser) .catch(error => error) } 意味着catch的调用者将不能错误findUserByEmail,因为在catch中捕获了任何可能的错误” s findUserByEmail。通常,最好允许错误渗入到函数的调用方中,例如,

catch

因此,除非您的someFunctionThatReturnsPromise('foobar') .then((result) => { // everything is normal, send the result res.send(result); }) .catch((err) => { // there was an error, set response status code to 500: res.status(500).send('there was an error'); }) findUserByEmail辅助函数需要在出现错误时进行特定的操作,否则最好返回{{1} }:

createNewUser

如果您的帮助器函数 do 在出现错误时需要执行某些操作,那么为确保错误正确传递给函数的调用者,我建议要么抛出错误Promise内:

const findUserByEmail = email => User.findOne(email);
const createNewUser = newUserDetails => new User(newUserDetails).save();

,以便在其他人呼叫catch时可以const findUserByEmail = email => User.findOne(email) .catch((err) => { // error handling - save error text somewhere, do a console.log, etc throw err; }); 。否则,如果您执行类似的操作

catch

然后,findUserByEmail的调用方必须检查const findUserByEmail = email => User.findOne(email) .catch((err) => { // do something with err return err; }); 中的如果结果实际上是一个错误,这很奇怪:

findUserByEmail

更好地将错误抛出到.then的{​​{1}}中,以便findUserByEmail('foo@bar.com') .then((result) => { if (result instanceof Error) { // do something } else { // No errors } }); 的使用者也可以 findUserByEmail

答案 1 :(得分:3)

在已有诺言的情况下用诺言构造函数创建诺言是没有意义的,这就是为什么将其称为诺言构造反模式的原因。

这是一个错误,reject("Error in findUserByEmail", error)reject仅接受1 论点,这是拒绝的原因。 error将被忽略。约定错误是Error对象而不是字符串。

该功能可以重构为:

   const findUserByEmail = (emailAddress) => {
     return User.findOne({email: emailAddress})
     .then(response => response) // noop
     .catch(error => {
        const readableError = new Error('Error in findUserByEmail');
        readableError.originalError = error;
        throw readableError;
      });
    })
  }

反模式不一定会导致性能下降,但会导致代码异味。它们使代码更难以阅读,维护和测试,也表明开发人员可能对该主题缺乏理解。

Promise构造函数对性能的影响很小。它引入了另一层嵌套,并导致了回调地狱-诺言应该有助于避免这种情况。

  

如果我在不添加新承诺的情况下更改了上述代码段<...>   那么我可能无法在findUserByEmail(“ test@example.com”)中进行.then和.catch吗?

不,一个诺言可以与then(...)catch(...)(这是then(null, ...)的语法糖)进行多次链接,这是该模式的优势。请注意,catch(err => { return err })catch(err => { throw err })是不同的东西,前者捕获错误,后者抛出错误。