我在设置结果对象的控制器中有一个异步方法。问题在于,不是等待await
执行完我的代码,而是跳到响应对象调用,该调用使未定义所需的变量。在调试器中,应该执行的方法中的断点在未定义的错误之后被命中。谁能解释为什么异步等待在这里不起作用?
控制器类中的方法:
public async loginUser(req: Request, res: Response) {
const { name, password } = req.body;
let result: ILoginResult = await UserData.login(name, password); // always undefined
res.status(result.status).send(result.result); // gets hit before result is set
}
UserData类:
import bcrypt from 'bcrypt';
import jwt from 'jsonwebtoken';
import mongoose from 'mongoose';
import ILoginResult from './ILoginResult';
import UserModel from '../../models/UserModel';
class UserData {
private connUri: string;
constructor() {
this.connUri = process.env.MONGO_LOCAL_CONN_URL;
}
public async login(name: string, password: string) {
try {
await mongoose.connect(this.connUri, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, }, (err) => {
let result: ILoginResult = { status: 0, result: null, error: '', token: '' };
let status = 200;
if (!err) {
UserModel.findOne({ name }, (err, user) => {
if (!err && user) {
// We could compare passwords in our model instead of below as well
bcrypt.compare(password, user.password).then(match => {
if (match) {
status = 200;
// Create a token
const payload = { user: user.name };
const options = { expiresIn: '2d', issuer: 'http://localhost' };
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, options);
// console.log('TOKEN', token);
result.token = token;
result.status = status;
result.result = user;
} else {
status = 401;
result.status = status;
result.error = `Authentication error`;
}
return result;
}).catch(err => {
status = 500;
result.status = status;
result.error = err;
return { status: status, result: result };
});
} else {
status = 404;
result.status = status;
result.error = err;
return result;
}
});
} else {
status = 500;
result.status = status;
result.error = err.toString();
return result;
}
});
} catch (e) {
let result: ILoginResult;
result.error = e.toString();
result.status = 500;
return result;
}
}
}
export default new UserData();
答案 0 :(得分:2)
请勿将async
/ await
直接与基于回调的API混合使用。
根据the documentation,mongoose.connect
确实返回一个诺言(前提是您使用的是最新版本),但是没有任何建议可以使它答应等待您给它的回调中发生的事情。
相反,请在await mongoose.connect
之后的代码中执行这些操作。
遵循这些原则(在UserData
中)
public async login(name: string, password: string) {
let result: ILoginResult = { status: 0, result: null, error: '', token: '' };
try {
await mongoose.connect(this.connUri, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, });
const user = await UserModel.findOne({ name });
let status = 200;
let match = await bcrypt.compare(password, user.password).catch(() => false);
if (!match) {
status = 401;
result.status = status;
result.error = `Authentication error`;
return result;
}
status = 200;
// Create a token
const payload = { user: user.name };
const options = { expiresIn: '2d', issuer: 'http://localhost' };
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, options);
// console.log('TOKEN', token);
result.token = token;
result.status = status;
result.result = user;
return result;
} catch (e) {
result.error = e.toString();
result.status = 500;
return result;
}
}
答案 1 :(得分:2)
await
在传统的节点样式回调中不起作用。它仅适用于Promises。由于您要提供回调,因此该回调将异步执行,而不会被await
等待。
此外,在已经使用await
并且函数返回诺言的情况下使用回调确实无法实现甚至使用await
的目的。编写没有回调的代码:
let result: ILoginResult = { status: 0, result: null, error: '', token: '' };
try {
await mongoose.connect(this.connUri, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true, });
let status = 200;
let user = await UserModel.findOne({ name });
if (user) {
// We could compare passwords in our model instead of below as well
let match = await bcrypt.compare(password, user.password);
if (match) {
status = 200;
// Create a token
const payload = { user: user.name };
const options = { expiresIn: '2d', issuer: 'http://localhost' };
const secret = process.env.JWT_SECRET;
const token = jwt.sign(payload, secret, options);
// console.log('TOKEN', token);
result.token = token;
result.status = status;
result.result = user;
} else {
status = 401;
result.status = status;
result.error = `Authentication error`;
}
return result;
} else {
status = 404;
result.status = 404;
result.error = err;
return result;
}
} catch (e) {
result.error = e.toString();
result.status = 500;
return result;
}
对我来说,这更清洁,减少了来自地狱的回调树,并且更优雅地使用了async / await。由于整个内容都包含在try / catch中,因此,如果发生任何await ...
错误,则外部catch将捕获该错误并返回带有消息的标准错误500,而无需在其中多次重复该代码。>