首页 > 解决方案 > 了解显式承诺构造反模式

问题描述

在我之前的帖子中强调的CertainPerformance建议我参考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

现在,我假设CertainPerformance 说过度使用了promise,因为 return new Promise((resolve, reject) => {当我已经在使用mongoose 的promise 时,我正在创建新的promise 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那么我可能无法.catch进入findUserByEmail("test@example.com")

在应用程序的 API 路由中,我将在其中调用该findUserByEmail("test@example.com")函数,如果出现错误,我会想做其他事情(对于不同的情况会有所不同,因此我不能在我的辅助函数中使用它)。

问题 3

是否,现在做return new Promise((resolve, reject) => {而不是只做一个 有意义,return User.findOne(还是我错过了什么?

标签: javascriptnode.jsmongoosepromise

解决方案


因为.findOne已经返回 a Promise,所以不需要用new Promise- 构造一个新的,只需根据需要用and链接到现有链上。这样的链可以有任意数量的s 和s - 仅仅因为你使用 a并不能阻止你在其他地方使用相同的 resolve 值。为了显示:Promise.then.catchPromise.then.catchPromise.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每次你想要能够使用 another 时都不需要构造 a .then。所以:

然后我可能无法在 findUserByEmail("test@example.com") 中使用 .then 和 .catch

.then是不正确的 - 您确实可以使用任意数量的s 和.catches链接到现有 Promise 的末尾。

注意 a.then只返回它的参数而不做任何其他事情(例如.then(currentUser => currentUser))是多余的——它根本不会做任何事情。另请注意, a.catch将捕获Promise 拒绝并解析为已解决 Promise。所以如果你这样做

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

catch意味着调用者 offindUserByEmail无法catch出错,因为任何可能的错误都在' findUserByEmails中捕获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');
  })

因此,除非您的findUserByEmailcreateNewUser辅助函数需要在出现错误时执行特定操作,否则最好只返回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是否实际上是一个错误,这很奇怪:

findUserByEmail('foo@bar.com')
  .then((result) => {
    if (result instanceof Error) {
      // do something
    } else {
      // No errors
    }
  });

最好把错误扔进findUserByEmail's catch,这样消费者findUserByEmail可以 .catch


推荐阅读