函数中的Mongoose查询承诺让代码变得丑陋

我正在创build我的用户API,我想检查用户名是否被使用。 所以我写了一个静态函数

static findByName(name) { const query = User.where({ username: name, }); query.findOne((queryErr, user) => { if (queryErr) { console.log(queryErr); return false; } return user; }); } 

当我在signUp中调用它

 signup(req, res) { if (!req.body.username || !req.body.password || !req.body.email) { return res.status(400).json({ success: false, message: 'Bad Request' }); } if (!Users.findByName(req.body.username)) { return res.status(409).json({ success: false, message: 'Username has been used' }); } const hashedPassword = this.genHash(req.body.password); const newUser = User({ username: req.body.username, }); } 

findByName返回未定义。 最后我用诺言。

  signup(req, res) { if (!req.body.username || !req.body.password || !req.body.email) { return res.status(400).json({ success: false, message: 'Bad Request' }); } return Users.findByName(req.body.username).then((existingUser) => { if (existingUser) { return res.status(409).json({ success: false, message: 'Username has been used' }); } const hashedPassword = this.genHash(req.body.password); const newUser = User({ username: req.body.username, password: hashedPassword, email: req.body.email, }); return newUser.save().then((user) => { res.json({ success: true, user }); }).catch((err) => { res.status(500).json({ success: false, message: 'Internal Server Error' }); }); }).catch((err) => { res.status(500).json({ success: false, message: 'Internal Server Error' }); }); } 

这是非常可怕的代码。 有没有更好的方法来清理代码?

有没有更好的方法来清理代码

是。 我将假设/signup被定义为通常的快速app实例上的POST路线

有了这个说法,因为你已经使用了promise,你可以更进一步,使用Node.js v7.6 +默认启用的async/await

这会让你的代码更加同步:

  async signup(req, res) { if (!req.body.username || !req.body.password || !req.body.email) { return res.status(400).json({ success: false, message: 'Bad Request' }); } try { const existingUser = await Users.findByName(req.body.username) if (existingUser) { return res.status(409).json({ success: false, message: 'Username has been used' }) } const hashedPassword = this.genHash(req.body.password); const newUser = await User({ username: req.body.username, password: hashedPassword, email: req.body.email, }).save() res.json({ success: true, newUser }); } catch (error) { res.status(500).json({ success: false, message: 'Internal Server Error' }); } } 

您可能已经注意到使用try/catch 。 这是因为我们没有使用.catch() ,我们仍然必须处理发生的任何错误。 为了进一步清理代码,我们可以编写一个error handling中间件来为我们处理错误:

src/middleware/error-handlers.js

 // Wraps the router handler, catches any errors, and forwards to the next middleware that handles errors exports.catchErrors = action => (req, res, next) => action(req, res).catch(next); // Notice the first parameter is `error`, which means it handles errors. exports.displayErrors = (error, req, res, next) => { const err = error; const status = err.status || 500; delete err.status; err.message = err.message || 'Something went wrong.'; if (process.env.NODE_ENV === 'production') { delete err.stack; } else { err.stack = err.stack || ''; } res.status(status).json({ status, error: { message: err.message, }, }); }; 

现在我们只需要use我们的error handling程序:

app.js

 const { catchErrors, displayErrors } = require('./middleware/error-handlers') // Whenever you defined the function, needs to have the `async` keyword async function signup(req, res) { ... } // Wrap the function call app.post('/signup', catchErrors(signUp)) // Handle any errors app.use(displayErrors) 

使用上述中间件将我们的代码转换为:

 async signup(req, res) { const error = new Error() if (!req.body.username || !req.body.password || !req.body.email) { error.status = 400 error.message = 'Bad Request' throw error } const existingUser = await Users.findByName(req.body.username) if (existingUser) { error.status = 409 error.message = 'Username has been used' throw error } const hashedPassword = this.genHash(req.body.password); const newUser = await User({ username: req.body.username, password: hashedPassword, email: req.body.email, }).save() res.json({ success: true, newUser }); } 

你可以看到代码是如何更容易阅读没有所有的噪音。

请务必阅读:

  • 编写用于Express应用程序的中间件
  • 快速error handling

我无法testing,我的解决scheme实际上可能(可能不是),或者我涵盖了您的原始代码的每个function。 我试图做的是制作处理特定事物的函数,然后将它们连接在一起,以便您可以看到更清晰的程序stream。

 signup(req, res) { if (!req.body.username || !req.body.password || !req.body.email) { return res.status(400).json({ success: false, message: 'Bad Request' }); } const handleError = error => res.status(500).json({ success: false, message: 'Internal Server Error' }); const handleExistingUser = existingUser => { if (existingUser) { return res.status(409).json({ success: false, message: 'Username has been used' }); } else { return Promise.resolve(); } } const handleCreateUser = () => { const hashedPassword = this.genHash(req.body.password); const newUser = User({ username: req.body.username, password: hashedPassword, email: req.body.email, }); return newUser.save().then((user) => { res.json({ success: true, user }); }); }; // the flow of the program is hopefully more clear here return Users.findByName(req.body.username) .then(handleExistingUser) .then(handleCreateUser) .catch(handleError); } 

如果你处理内部和外部的错误的承诺是一样的,那么我认为这是足够的在外层的error handling。 (在你的最后一个例子中)但是我不是100%确定的。

你的函数返回undefined ,因为它没有return语句。 return user语句是findOnecallback函数的(无用的)返回值,而不是findByName

如果你去承诺,那么定义这个函数如下:

 static findByName(name) { return User.where({ username: name }).findOne().exec(); } 

你的承诺链可以简化一下,就像这样:

 signup(req, res) { function report(message, status = 200) { res.status(status).json({ success: status === 200, message }); } if (!req.body.username || !req.body.password || !req.body.email) { return report('Bad Request', 400); } Users.findByName(req.body.username).then((existingUser) => { return existingUser ? null // treat this condition in the next `then` : User({ username: req.body.username, password: this.genHash(req.body.password), email: req.body.email, }).save().exec(); }).then((user) => { return existingUser ? report(user) : report('Username has been used', 409); }).catch((err) => { report('Internal Server Error', 500); }); }