理解显式承诺构造反模式

iRo*_*tia 1 javascript mongoose node.js promise

上一篇文章中强调的某些性能建议我参考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)
      })
    })
}
Run Code Online (Sandbox Code Playgroud)

问题1

现在,我假设 certainPerformance 说的是过度使用承诺,因为 return new Promise((resolve, reject) => {当我已经在 mongoose 中使用承诺时,我正在创建新的承诺User.findOne({email: emailAddress}).then(response => {

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

const { findUserByEmail } = require("./my_db_query");
Run Code Online (Sandbox Code Playgroud)

我可能希望它返回响应或在出现错误时抛出错误

findUserByEmail("test@example.com").then(/*...*/).catch(/*...*/);
Run Code Online (Sandbox Code Playgroud)

如果我更改上面的代码片段而不添加新的承诺

 function findUserByEmail  (email) {
       return User.findOne({email: email}).then(currentUser => currentUser).catch(error => error)
    } 
Run Code Online (Sandbox Code Playgroud)

问题2

那我可能就不能.then进去.catchfindUserByEmail("test@example.com")

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

问题3

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

Cer*_*nce 5

因为.findOne已经返回了 a Promise,所以不需要用 - 构造一个新的new Promise- 相反,只需根据需要用和链接到现有Promise链上即可。这样的链可以有任意数量的s 和s - 仅仅因为您使用 1并不能阻止您在其他地方使用相同的解析值。为了显示:.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
  });
Run Code Online (Sandbox Code Playgroud)

换句话说 - 无需new Promise每次您希望能够使用另一个 时都构造 a .then。所以:

那么我可能无法在 findUserByEmail("test@example.com") 中进行 .then 和 .catch

.then不正确 - 你确实可以用任意数量的s 和es链接到现有 Promise 的末尾.catch

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

function findUserByEmail(email) {
  return User.findOne({email: email})
    .then(currentUser => currentUser)
    .catch(error => error)
}
Run Code Online (Sandbox Code Playgroud)

catch意味着 的调用者findUserByEmail无法出错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');
  })
Run Code Online (Sandbox Code Playgroud)

因此,除非您的函数findUserByEmailcreateNewUser辅助函数在出现错误时需要执行特定的操作,否则最好只返回Promise单独的内容:

const findUserByEmail = email => User.findOne(email);
const createNewUser = newUserDetails => new User(newUserDetails).save();
Run Code Online (Sandbox Code Playgroud)

如果您的辅助函数确实需要在出现错误时执行某些操作,那么为了确保错误正确传递给函数的调用者,我建议将错误抛出catch

const findUserByEmail = email => User.findOne(email)
  .catch((err) => {
    // error handling - save error text somewhere, do a console.log, etc
    throw err;
  });
Run Code Online (Sandbox Code Playgroud)

这样您就可以catch在有其他事情需要时findUserByEmail。否则,如果你做类似的事情

const findUserByEmail = email => User.findOne(email)
  .catch((err) => {
    // do something with err
    return err;
  });
Run Code Online (Sandbox Code Playgroud)

那么调用者findUserByEmail必须检查结果.then是否确实是一个错误,这很奇怪:

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

最好将错误抛出findUserByEmail's catch,这样 's 的消费者findUserByEmail可以。 .catch