有没有更好的方法来编写这段代码? - 使用NodeJS中的Promise链接HTTP请求 - 使用Await / Async?

时间:2017-11-12 05:06:12

标签: javascript node.js mongodb asynchronous promise

我正在使用NodeJS和Express,通过查询数据库在网页上动态生成HTTP。该页面将通过遍历MongoDB集合并使用数据库中的各种属性填充元素属性和内容来加载元素。 PUG / Jade允许迭代返回的Database JSON以生成元素

起初我只需要查询一个集合,但在需要查看两个或更多集合时遇到了麻烦。

我正在链接回调,但第一个查询中的数据卡在第一个闭包中。我切换到Github(https://github.com/request/request-promise

上方便的请求 - 承诺框架

我使用.then()子句,但我仍然丢失了返回值。我以为我可以运行res.render两次,但如果第二个数据库查询仍未定义,它不允许我生成部分网页。

router.get('/main', authorize.adminRequired, function(req, res, next) {
    rp.get({
        url: 'http://localhost:3000/menus'
    }, function(err, response, menuItems) {
            console.log(menuItems); 
            res.render('index', {menu: menuItems}) //PUG error, PUG variable "history" is undefined
    }).then(rp.get({
        url: 'http://localhost:3000/transactions'   
    }, function(err, response, transactions, menuItems) { //menuItems is now Undefined
    console.log(transactions);
    return res.render('index', {menu: JSON.parse(menuItems), history: JSON.parse(menuItems)});
    }));
});

我设法更改代码以使其正常工作。以下代码似乎没问题。但有没有更好的方法来使用await写出来并传递该值,而不是将这些函数链接为新变量?我真的不知道如何使用Promise。

router.get('/main', authorize.adminRequired, function(req, res, next) {
    var getMenuItems = rp.get({
            url: 'http://localhost:3000/menus'
        }, function(err, response, menuItems) {
            console.log(menuItems);
            return menuItems;
    });

    getMenuItems.then(function(result){
        rp.get({
            url: 'http://localhost:3000/transactions'   
        }, function(err, response, transactions) {
            console.log(transactions);
            return res.render('index', {menu: JSON.parse(result), history: JSON.parse(transactions)});
        });
    });
});

2 个答案:

答案 0 :(得分:1)

首先,这段代码

}).then(rp.get({

是错误的,因为.then接受1(或2)个参数,但如果它们不是函数

,则默默地忽略它们(即不产生错误)

像你一样使用.then,你传递调用 rp.get的结果 - 这是一个Promise,而不是一个函数

所以,正确使用请求承诺:

router.get('/main', authorize.adminRequired, function(req, res, next) {
    rp.get({
        url: 'http://localhost:3000/menus'
    })
    .then(menuItems => rp.get({ url: 'http://localhost:3000/transactions'}).then(transactions => ({transactions, menuItems})))
    .then(({transactions, menuItems}) => {
        // now do whatever with transactions and menuItems
    })
});

使用async / await

router.get('/main', authorize.adminRequired, async function(req, res, next) {
    let menuItems = await rp.get({url: 'http://localhost:3000/menus'});
    let transactions = await rp.get({ url: 'http://localhost:3000/transactions'});
    // now do whatever with transactions and menuItems
});

答案 1 :(得分:0)

由于你的两个requests不相互依赖,你可以利用Promise.all()来让你并行而不是顺序地请求它们 - 并使代码更简单:

router.get('/main', authorize.adminRequired, function(req, res, next) {        
    Promise.all([
            rp.get('http://localhost:3000/menus'),
            rp.get('http://localhost:3000/transactions')
        ])
        .then(([menus, transactions]) => {
            // now you have menus and transaciotns
            res.render(/* use menus and transactions */ )
        })
})