【问题标题】:if/else with async code in one caseif/else 在一种情况下使用异步代码
【发布时间】:2019-01-15 15:53:46
【问题描述】:

我在异步代码方面没有太多经验,我遇到了一个问题,这是我的代码:

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

如您所见,问题在于,在一种情况下,我只需要做一个简单的程序操作,在另一种情况下,我必须查询异步数据库,然后我必须执行下面的代码。我不能简单地将 Account 查询放在 User 查询的回调中,因为我不需要一直执行 User 查询。 我试图在这里找到答案,但我发现的唯一结果是关于执行一个或另一个异步任务(例如:Working with promises inside an if/else)。 按照这篇文章的建议,我考虑将代码包装在匿名函数中的 if 块中,并执行以下操作:

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

但我觉得这很奇怪,我想我需要从 if 块中的匿名函数返回一个 Promise。

那么解决这个问题的优雅方法是什么?我在空闲时间学习 node.js,任何一般性建议将不胜感激。

感谢您的宝贵时间。

【问题讨论】:

    标签: node.js express asynchronous


    【解决方案1】:

    回调不应与 Mongoose 一起使用;支持 promise 很久了。

    这个

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

    不会起作用,因为 get_user 预计会返回一个承诺。

    通常是这样的:

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

    请注意,req.params.user 是这两种情况的通用代码。这可以通过 async..await 方便地完成,它是 Promise 的语法糖:

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

    这应该是async中间件函数的主体,原始代码中没有显示。

    正如this answer 中所述,Express 本身不支持 Promise,所有async 中间件和路由处理程序都应使用try..catch 包装以进行错误处理。

    【讨论】:

    • 好的,昨天我做了类似的事情来使它工作: if (!req.params.user_name) { get_user = () => { return new Promise((resolve) => { 相同的else 块,然后会在内部使用 Account 请求执行“get_user().then(() => {”。因此,通过阅读您的代码,我发现我不需要在 if 块中实例化 Promise,只需调用它是静态的,我不需要将它包装在一个匿名函数中,对于我将在其中解析 User.findOne 的结果的 else 块也是如此。你确认了吗?放同步代码对我来说有点不安变成 Promise 但现在更有意义了。
    • 至于 async..await 示例是否意味着如果我不放置回调,User.findOne 将自动返回结果,如果有则自动抛出错误?这会很棒而且更直观,因为它看起来像同步代码。谢谢你的解释,它真的把我脑子里的东西都弄清楚了。
    • 是的,这里根本不需要匿名函数。您需要Promise.resolve 以防您使用原始承诺,因为您需要与then 链接的东西,这就是为什么在第一个sn-p 中有userPromise。 User.findOne 返回结果的承诺await 是 .then(result => /* handle result */, err => /* throw an error */) 的语法糖。两个 sn-ps 在功能上是等效的,第二个 sn-p 看起来更简单。由于您使用的是支持 async/await 的 Node,我建议您坚持使用 async/await。
    • 好吧,我会试试 async..await,这可以让我清理很多我到处都有的嵌套回调。感谢您的宝贵时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-08-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-28
    相关资源
    最近更新 更多