jeninja
jeninja

Reputation: 848

Promise not terminating after reject() call

Hi I am trying to check for invalid cases before executing a database action. For some reason my code will run reject('Invalid E-mail address.'); but then it will also continue to run the await User.findOne({email}) line. There are several ways I can rewrite this but I would like to keep the current structure of this code. How can I terminate the function after reject and prevent the rest of the code from running? Thanks

user.database.ts

export const registerUser = async (email: string, password: string): Promise<User> => {
    return new Promise(async (resolve, reject) => {
        // check if email or password are null
        if (!email || !password) {
            reject('Invalid E-mail address or Password.');
        }

        // check if email is invalid
        if (!EmailValidator.validate(email)) {
            reject('Invalid E-mail address.');
        }

        // check if database already contains E-mail address
        await User.findOne({email}).then((user: any) => {
            if (user) {
                reject('E-mail address already in use.'); // code should stop here
            }
        });

        // anything below should not run

        // create user object
        const user = new User({
            _id: new mongoose.Types.ObjectId(),
            email: email,
            password: password
        });

        // save user object to database
        await user.save().then((result: any) => {
            resolve(result);
        }).catch((error: any) => {
            reject(error);
        });
    });
};

auth.controller.ts

export const register = (req: Request, res: Response) => {
    const email = req.body.email;
    const password = req.body.password;
    registerUser(email, password).then((user: User) => {
        res.status(200).json(user);
    }).catch((error: any) => {
        res.status(300).json(error);
    });
};

Upvotes: 0

Views: 57

Answers (1)

trincot
trincot

Reputation: 350272

Returning new Promise() inside an async function is an antipattern: an async function already returns a promise, so you only need to explicitly return the value that promise should resolve to.

The issue you describe is caused by the fact that you don't exit your function once you know its failure reason: the function will still continue the rest of the code if you don't also return or throw.

Tackling both issues, your code can be rewritten as follows:

const registerUser = async (email: string, password: string): Promise<User> => {
    if (!email || !password) {
        throw new Error('Invalid E-mail address or Password.');
    }

    if (!EmailValidator.validate(email)) {
        throw new Error('Invalid E-mail address.');
    }

    let user: any = await User.findOne({email})
    if (user) {
        throw new Error('E-mail address already in use.');
    }

    user = new User({
        _id: new mongoose.Types.ObjectId(),
        email: email,
        password: password
    });

    return user.save();
};

Note that throw inside an async function will make the promise (that the async function always returns) reject with the reason you provide.

Note also that in the case of success, the resolution value is here a promise (user.save()), so that means the promise of the async function will link its resolution to the resolution of that promise.

Upvotes: 2

Related Questions