user3803848
user3803848

Reputation: 175

if/else with async code in one case

I don't have much experience in async code and I'm stuck with a problem, here is my code :

if (!req.params.user_name) {
    req.params.user = req.me;
} else {
    User.findOne({username: req.params.user_name}, '_id', (error, user) => {
        if (error) {
            return next(error);
        }
        if (!user) {
            let error = new Error('User not found');
            return next(error);
        }
        req.params.user = user;
    });
}
Account.findOne({name: req.params.account_name, created_by: req.params.user})
.populate(['currency', 'created_by'])
.exec((err, account) => {
    if (err) {
        return next(err);
    }
    return res.send(account);
});

As you can see the problem is that in one case I just have a simple procedural action to do, in the other I have to query the database which is async, then I have to execute the code below. I can't just simply put the Account query in the callback of the User query because I don't need to execute User query all the time. I've tried to find an answer here but the only results I've found are about executing one async task or another (ex: Working with promises inside an if/else). Following the recommandations on this post I've thought about wrapping the code inside the if block in an anonymous function and do something like:

let get_user;
if (!req.params.user_name) {
    let get_user = () => {req.params.user = req.me};
} else {
    let get_user = User.findOne({username: req.params.user_name}, '_id', (error, user) => {
        if (error) {
            return next(error);
        }
        if (!user) {
            let error = new Error('User not found');
            return next(error);
        }
        req.params.user = user;
    });
}
get_user().then(() => {
    Account.findOne({name: req.params.account_name, created_by: req.params.user})
        .populate(['currency', 'created_by'])
        .exec((err, account) => {
            if (err) {
                return next(err);
            }
            return res.send(account);
        });
});

But I find it weird and I guess I would need to return a Promise from the anonymous function in the if block.

So what would be an elegant way of solving this ? I'm learning node.js on my free time and any general suggestions would be greatly appreciated.

Thanks for your time.

Upvotes: 1

Views: 2647

Answers (1)

Estus Flask
Estus Flask

Reputation: 222369

Callbacks shouldn't be used with Mongoose; it has been supporting promises for a long time.

This

let get_user = () => {req.params.user = req.me};

won't work because get_user is expected to return a promise.

It's usually done like:

let userPromise;
if (!req.params.user_name) {
    userPromise = Promise.resolve(req.me);
} else {
    userPromise = User.findOne(...);
}
userPromise().then((user => {
    req.params.user = user;

    return Account.findOne(...);
});

Notice that req.params.user is common code for both conditions. This is conveniently done with async..await, which is syntactic sugar for promises:

try {
    let user;
    if (!req.params.user_name) {
        user = req.me;
    } else {
        user = await User.findOne({username: req.params.user_name}, '_id');
    }

    req.params.user = user;

    const account = await Account.findOne({name: req.params.account_name, created_by: req.params.user})
        .populate(['currency', 'created_by'])
        .exec();

    res.send(account);
} catch (err) {
    next(err);
}

This is supposed to be a body of async middleware function, which wasn't shown in original code.

As explained in this answer, Express doesn't support promises itself, all async middlewares and route handlers should be wrapped with try..catch for error handling.

Upvotes: 1

Related Questions