好吧,我是这个网站的常客,通常只是找东西,而不是问他们。
但这会让我发疯,我是第一个使用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");
})
});
答案 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的结合,我相信这对大多数人来说都是基础知识,但是我对此并不陌生。另外,对此没有具体答案,希望这对其他人有帮助。
还是谢谢你!