Node.js使用另一个查询的结果执行查询

时间:2018-06-16 22:16:07

标签: sql-server node.js

好吧,我是这个网站的常客,通常只是找东西,而不是问他们。

但这会让我发疯,我是第一个使用Node.js的计时器,我实际上只是因为我真的需要它而使用它。

问题如下:

我有一个sql server数据库,关系和所有。我需要检索有关餐馆的信息。

我划分信息的方式是处理餐馆名称和单独的餐桌特许经营权。

我正在为每家餐厅获得所有餐厅特许经营权,但是当我想将它们与餐馆名称配对时(当然,这不仅仅是一个名字)它继续把最后一个放在名单上,我知道这种情况发生是因为查询是以异步方式执行的。

我已经尝试了大约一个半星期来解决这个问题,但我在node.js的专业知识接近于0而且我无法理解它是如何神奇地运作的......

我会粘贴下面的工作代码,有人有想法吗?我尝试使用promises并且最近asyn.EachSeries但我不知道如何正确使用它。

sql.connect(config, function(err){

    if(err) console.log(err);

    var request = new sql.Request();

    request.execute('sp_GetRestaurants')
    .then( rows => {

        rows.recordset.forEach( function(restaurant) {

            Restaurant = {}
            var restaurantInfo = new Array();

            Restaurant.iRestaurantCode = restaurant.id_restaurant;
            Restaurant.sRestaurantName = restaurant.restaurant_name;
            Restaurant.sRestaurantWebsite = restaurant.restaurant_website;
            Restaurant.bEnabled = restaurant.enabled;
            Restaurant.sRestaurantLogo = restaurant.logo;
            Restaurant.sCountry = restaurant.country_name;
            Restaurant.flRestaurantAvg = restaurant.restaurant_avg;

            var infoRequest = new sql.Request();
            infoRequest.input('restaurant_id', restaurant.id_restaurant);
            console.log("por aca")
            infoRequest.execute('sp_GetRestaurantInfo')
            .then( ri => {

                    ri.recordset.forEach( function(info) {
                    Franchise = {};

                    Franchise.iFranchiseID = info.id_address;
                    Franchise.sFranchiseLocation = info.location_desc;
                    Franchise.sFranchiseAddress = info.address_desc;
                    Franchise.sFranchiseNumber = info.phone_number;
                    Franchise.bFranchiseDelivery = info.yn_delivery;
                    Franchise.nLongitude = info.longitude;
                    Franchise.nLatitude = info.latitude;

                    restaurantInfo.push(Franchise);
                  //console.log(restaurantInfo)  

                });

            })
            .then(() => {
                Restaurant.alFrRestaurant = restaurantInfo;
                console.log(Restaurant);
                //db.collection('Restaurant').doc().set(Restaurant)
            })
        })
        return rows;
    })
    .then( () => {
        console.log("Finished");
    })
});

4 个答案:

答案 0 :(得分:0)

试试这个:npm install bluebird,然后在你的代码中(编辑:我在最后添加了解释):

// Add this at the top of your file
var Promise = require('bluebird')

// ... more code ...

sql.connect(config, function(err){

    if(err) console.log(err);

    var request = new sql.Request();

    request.execute('sp_GetRestaurants')
    .then( rows => Promise.map(rows.recordset, function(restaurant) {

        var Restaurant = {}

        Restaurant.iRestaurantCode = restaurant.id_restaurant;
        Restaurant.sRestaurantName = restaurant.restaurant_name;
        Restaurant.sRestaurantWebsite = restaurant.restaurant_website;
        Restaurant.bEnabled = restaurant.enabled;
        Restaurant.sRestaurantLogo = restaurant.logo;
        Restaurant.sCountry = restaurant.country_name;
        Restaurant.flRestaurantAvg = restaurant.restaurant_avg;

        var infoRequest = new sql.Request();
        infoRequest.input('restaurant_id', restaurant.id_restaurant);
        console.log("por aca")
        return infoRequest.execute('sp_GetRestaurantInfo')
        .then( ri => ri.recordset.map( function(info) {
            Franchise = {};

            Franchise.iFranchiseID = info.id_address;
            Franchise.sFranchiseLocation = info.location_desc;
            Franchise.sFranchiseAddress = info.address_desc;
            Franchise.sFranchiseNumber = info.phone_number;
            Franchise.bFranchiseDelivery = info.yn_delivery;
            Franchise.nLongitude = info.longitude;
            Franchise.nLatitude = info.latitude;

            return Franchise;

        }))
        .then(restaurantInfo => {
            Restaurant.alFrRestaurant = restaurantInfo;
            console.log(Restaurant);
            return Restaurant;
        })
    }))
    .then( restaurants => {
        console.log("Finished");
        console.log(restaurants);
    })
});

在原始代码中,您尝试通过在您的餐馆结果集上执行forEach来获得多个查询结果。它无法工作,因为后续查询(在forEach中)也是异步代码。

我在上面的代码中建议您在第一个结果集上使用Promise.map,每个结果集返回一个承诺:infoRequest.execute('sp_GetRestaurantInfo')返回的承诺。

答案 1 :(得分:0)

如果我理解你的问题,你想在查询餐厅后查询特许经营权。

要做到这一点,我自己会使用名为bluebird的npm包。该软件包为javascript本机Promise添加了有用的功能。对于这个例子,我将使用bluebird提供的Promise.all()函数。此函数允许您返回承诺数组。当使用Promise.all函数时,只有在给予Promise.all()函数的promise数组中的所有promise都已解决时(或者如果有错误就被拒绝),才会调用“.then”函数。

我已调整您的代码以使用bluebird并希望解决您的问题。我使用Promise.all()两次。

第一次只是为了让我可以将创建的“Restaurant”对象赋予“infoRequest.execute('sp_GetRestautantInfo')”的promise解析处理程序。 Promise.all的这种使用也使用了“.spread”。它具有与“.then”相同的功能,但它只是扩展了promises数组,而不必通过数组的索引访问它们。这就是“.spread”有两个参数的原因。

我第二次使用Promise.all()是返回一个promises数组,每个promise将解析为一个Restaurant对象。 “.then”将被赋予餐馆阵列。

希望这有助于解决您的问题。

var Promise = require("bluebird");

sql.connect(config, function (err) {

    if (err) console.log(err);

    var request = new sql.Request();

    request.execute('sp_GetRestaurants')
        .then(rows => {

            var promises = new Array();
            rows.recordset.forEach((restaurant) => {

                Restaurant = {};

                Restaurant.iRestaurantCode = restaurant.id_restaurant;
                Restaurant.sRestaurantName = restaurant.restaurant_name;
                Restaurant.sRestaurantWebsite = restaurant.restaurant_website;
                Restaurant.bEnabled = restaurant.enabled;
                Restaurant.sRestaurantLogo = restaurant.logo;
                Restaurant.sCountry = restaurant.country_name;
                Restaurant.flRestaurantAvg = restaurant.restaurant_avg;

                var infoRequest = new sql.Request();
                infoRequest.input('restaurant_id', restaurant.id_restaurant);
                console.log("por aca")
                promises.push(Promise.all([Restaurant, infoRequest.execute('sp_GetRestaurantInfo')])
                    .spread((Restaurant, ri) => {
                        var restaurantInfo = new Array();
                        ri.recordset.forEach((info) => {
                            Franchise = {};

                            Franchise.iFranchiseID = info.id_address;
                            Franchise.sFranchiseLocation = info.location_desc;
                            Franchise.sFranchiseAddress = info.address_desc;
                            Franchise.sFranchiseNumber = info.phone_number;
                            Franchise.bFranchiseDelivery = info.yn_delivery;
                            Franchise.nLongitude = info.longitude;
                            Franchise.nLatitude = info.latitude;

                            restaurantInfo.push(Franchise);
                            //console.log(restaurantInfo)  

                        });
                        Restaurant.alFrRestaurant = restaurantInfo;
                        console.log(Restaurant);
                        return Restaurant;
                        //db.collection('Restaurant').doc().set(Restaurant)
                    }));
            })
            return Promise.all(promises);
        })
        .then((restaurants) => {
            // restaurants is an array of all restaurants with their Franchises
            console.log("Finished");
        })
});

答案 2 :(得分:0)

让你前进的一些提示。

在建立一个保证链时,具有异步代码的链中的每个“链接”都应该return一个Promise。所以当你infoRequest.execute('sp_GetRestaurantInfo').then()时,因为那是新的Promise,它必须从现有的.then()块中返回,否则什么都不会等待它。

不要以异步方式使用标准数组.forEach。如果使用Promises,请将数组映射到Promise.all()的Promises数组,使用bluebird的Promise.each或使用async/await,使用for(let item of array){}

我希望有所帮助。正如我确定你在console.log语句中看到的那样,事情没有按照你期望的顺序执行。从您发布的示例中,最大的罪魁祸首是不会在.then()内回复您的新承诺。

答案 3 :(得分:0)

好吧,在我发布问题2小时后,我从字面上找到了解决方案。

它会像这样

getRestaurants(function (err, rest){
    if(err){
        console.log(err);
        return;
    }

    //console.log(rest);
    sql.close();
    getRestaurantInfo(rest, function(err, resto) {
        if(err) {
            console.log(err);
            return;
        }

        //console.log(resto);
        sql.close();
    })
})

function getRestaurants(getRestaurantCB){

    sql.connect(config, function(err) {

        if(err) {
            console.log("Connection error ", err);
            getRestaurantCB(err);
            return;
        }

        console.log("Connected to databasse");

        var request = new sql.Request();

        request.execute('sp_GetRestaurants')
        .then( rows => {
            console.log("getRestaurant done");
            getRestaurantCB(null, rows);
        })
    })    
}

function getRestaurantInfo(restaurants, getRestaurantInfoCB) {

    sql.connect(config, function(err) {

        if(err) {
            console.log("Connection error ", err);
            getRestaurantInfoCB(err);
            return;
        }

        var restaurantInfo = new Array();
        async.eachSeries(restaurants.recordset, function (id, cb) {
            setTimeout(function() {

                Restaurant = {}
                var restaurantInfo = new Array();

                Restaurant.iRestaurantCode = id.id_restaurant;
                Restaurant.sRestaurantName = id.restaurant_name;
                Restaurant.sRestaurantWebsite = id.restaurant_website;
                Restaurant.bEnabled = id.enabled;
                Restaurant.sRestaurantLogo = id.logo;
                Restaurant.sCountry = id.country_name;
                Restaurant.flRestaurantAvg = id.restaurant_avg;

                var infoRequest = new sql.Request();
                infoRequest.input('restaurant_id', id.id_restaurant);
                infoRequest.execute('sp_GetRestaurantInfo')
                .then( ri => {
                        ri.recordset.forEach( function(info) {
                        Franchise = {};

                        Franchise.iFranchiseID = info.id_address;
                        Franchise.sFranchiseLocation = info.location_desc;
                        Franchise.sFranchiseAddress = info.address_desc;
                        Franchise.sFranchiseNumber = info.phone_number;
                        Franchise.bFranchiseDelivery = info.yn_delivery;
                        Franchise.sLongitude = info.longitude;
                        Franchise.sLatitude = info.latitude;

                        restaurantInfo.push(Franchise);                        
                    });

                })
                .then(() => {
                    Restaurant.alFrRestaurant = restaurantInfo;
                    console.log(Restaurant);
                    db.collection('Restaurant').doc().set(Restaurant)
                    return cb();
                })
            }), 5000;
        }, function (err) {
            console.log("Final call");
            getRestaurantInfoCB(null, Restaurant);  
        });
    })
}

它运行完美。它是回调函数与async.EachSeries的结合,我相信这对大多数人来说都是基础知识,但是我对此并不陌生。另外,对此没有具体答案,希望这对其他人有帮助。

还是谢谢你!