首页 > 解决方案 > 在 TypeScript API 中跳过异步等待

问题描述

我在设置结果对象的控制器中有一个异步方法。问题不是等待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
  }

用户数据类:

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();

标签: javascripttypescriptrest

解决方案


不要将async/await直接与基于回调的 API 混合使用。

根据文档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;
    }
}

推荐阅读