Node.JS+;猫鼬回叫地狱
如何将mongoose保存到db,但等待其他集合先加载? 平台和流派为空,因为“保存”功能在加载平台和流派之前运行,请帮助Node.JS+;猫鼬回叫地狱,node.js,mongodb,asynchronous,mongoose,Node.js,Mongodb,Asynchronous,Mongoose,如何将mongoose保存到db,但等待其他集合先加载? 平台和流派为空,因为“保存”功能在加载平台和流派之前运行,请帮助 var platforms = []; //load platforms body.release_dates.forEach(function(elem){ Platform.findOne({ id : elem.platform}, function(err, result) { platforms.push(mongoose.Type
var platforms = []; //load platforms
body.release_dates.forEach(function(elem){
Platform.findOne({ id : elem.platform}, function(err, result) {
platforms.push(mongoose.Types.ObjectId(result._id));
});
});
var genres = []; //load genre
body.genres.forEach(function(elem){
Genre.findOne({id: elem}, function(err, result){
genres.push(mongoose.Types.ObjectId(result._id));
})
});
//prepare to save!
var game = {
igdb_id : body.id,
name : body.name,
summary : body.summary,
storyline : body.description,
genres : genres,
platforms : platforms, // <- genres amd platforms empty and not wait platforms and genre array to complete
release_date : body.original_release_date,
cover : body.cover.cloudinary_id,
videos: body.videos
};
var data = new Game(game);
data.save(function(err, game){
if(err){
res.send("500");
return console.error(err);
}
});
}
var平台=[]//装载平台
body.release\u dates.forEach(函数(elem)){
findOne({id:elem.Platform},函数(err,result){
push(mongoose.Types.ObjectId(result.\u id));
});
});
变量类型=[]//加载类型
body.genres.forEach(函数(elem){
findOne({id:elem},函数(err,result){
tyres.push(mongoose.Types.ObjectId(result.\u id));
})
});
//准备保存!
var博弈={
igdb_id:body.id,
name:body.name,
总结:body.summary,
故事情节:body.description,
类型:类型,
平台:平台,//这是promises的一个不错的用例(这是一个非常好的工具,使您能够轻松地执行异步操作),将来应该会对您有所帮助
当前代码的问题是,findOne
操作是异步的,会在一段时间后完成。同时,下一行将开始执行。因此,当您达到save
状态时,findOne
将不会完成,并且会得到空数组
实现承诺的两个流行nodejs库是和。nodejs的最新版本也实现默认值
以下是使用Bluebird的代码。基本上,您必须在平台和类型中为涉及findOne
的每个数据库操作创建承诺。当所有这些操作完成后,您必须开始执行最后的save
部分。这是使用等待所有承诺完成的功能实现的完成
var Promise = require('bluebird')
var platformPromises = []; //load platforms
body.release_dates.forEach(function(elem){
platformPromises.push(new Promise(function (resolve, reject) {
Platform.findOne({ id : elem.platform}, function(err, result) {
if(err)
reject(err);
else
resolve(mongoose.Types.ObjectId(result._id));
});
}))
});
var genrePromises = []; //load genre
body.genres.forEach(function(elem){
genrePromises.push(new Promise(function (resolve, reject) {
Genre.findOne({id: elem}, function(err, result){
if(err)
reject(err);
else
resolve(mongoose.Types.ObjectId(result._id));
});
}))
});
var allPromises = platformPromises.concat(genrePromises);
Promise.all(allPromises).then(function (result) {
//prepare to save!
var platforms = [];
var genres = [];
for(var i=0; i<platformPromises.length; i++)
platforms.push(result[i]); // result come out in same order as the promises
for(var i=platformPromises.length; i<result.length; i++)
genres.push(result[i]);
var game = {
igdb_id : body.id,
name : body.name,
summary : body.summary,
storyline : body.description,
genres : genres,
platforms : platforms,
release_date : body.original_release_date,
cover : body.cover.cloudinary_id,
videos: body.videos
};
var data = new Game(game);
data.save(function(err, game){
if(err){
res.send("500");
return console.error(err);
}
});
})
var Promise=require('bluebird'))
var platformPromises=[];//加载平台
body.release\u dates.forEach(函数(elem)){
platformPromises.push(新承诺(函数)(解析、拒绝){
findOne({id:elem.Platform},函数(err,result){
如果(错误)
拒绝(错误);
其他的
解析(mongoose.Types.ObjectId(result._id));
});
}))
});
var genrePromises=[];//加载类型
body.genres.forEach(函数(elem){
genrePromises.push(新承诺(功能)(解析、拒绝){
findOne({id:elem},函数(err,result){
如果(错误)
拒绝(错误);
其他的
解析(mongoose.Types.ObjectId(result._id));
});
}))
});
var allPromises=platformPromises.concat(genrePromises);
承诺。所有(所有承诺)。然后(函数(结果){
//准备保存!
var平台=[];
变量类型=[];
对于(var i=0;i您可以使用异步模块来完成这项工作,它非常适合完成这类任务
var async = require ('async');
var platforms = [];
var genres = [];
async.parallel([
function(cb){
body.release_dates.forEach(function(elem){
Platform.findOne({ id : elem.platform}, function(err, result){
cb(null,mongoose.Types.ObjectId(result._id))
});
});
},
function(cb){
body.genres.forEach(function(elem){
Genre.findOne({id: elem},enter code here function(err, result){
cb(null,mongoose.Types.ObjectId(result._id));
})
});
}],function(err,results){
//here you'll get an array of results ordered by your tasks
if(!err){
platforms.push(results[0])
genres.push(results[1])
}
})
我没有运行此代码,但就是这样,如果您需要更多信息,可以阅读文档:好的,首先,mongoose(至少是任何最新版本)如果您停止回调,则已支持承诺…其次,下面的示例使用承诺与异步功能相结合。这在Node 7+中的选项标志后面,因此您应该使用babel进行传输
我将评论放在了应该优化mongodb调用的地方,但将逻辑尽可能地放在上面,希望这对您有所帮助
关键的外卖是
- 使用承诺,不要害怕创建额外的函数来打破逻辑
- Promise.all可用于等待并行操作完成
async
函数非常棒
代码:
//将异步将发布日期元素映射到平台
异步函数GetPlatform(发布日期){
//TODO:更改为仅包含所需属性的单个查询
返回等待承诺。全部(releaseDates.map(
elem=>Platform.findOne({id:elem.Platform})
));
}
//将您的类型列表异步映射到适当的ObjectId对象中
异步函数getGenres(genres){
//TODO:更改为在单个查询中仅返回单个属性
var genres=wait Promise.all(genres.map(elem=>Genre.findOne({id:elem}));
返回genres.map(result=>mongoose.Types.ObjectId(result.\u id));
}
//异步请求处理程序(始终将try/catch用于express)
//不确定当前/将来的版本是否允许
//处理程序/错误
异步函数saveGameDetails(req、res){
试一试{
//数组非结构化赋值,分解数组
//等待将等待承诺,承诺。所有将采取阵列
//把它们包装成一个承诺。
var[platforms,tyres]=等待承诺([
获取平台(正文发布日期),
getGenres(body.genres)
]);
//准备保存!
var博弈={
igdb_id:body.id,
name:body.name,
总结:body.summary,
故事情节:body.description,
类型:类型,
platforms:platforms,//Mongoose已经在退出回调函数时返回了承诺。感谢您指出这一点。我只是想解释一下如何为来自同步背景的人使用承诺。@hyades,很好,但是如果您要获得三个数组,您只需在“result”中混合platformPromises和genrePromises,结果是什么“for”函数看起来像是三个数组?例如,platformPromises、genrePromises、,themePromises@tonywei是,结果数组的顺序与输入数组的顺序相同,
// will asynchronously map your release date elements to the Platform
async function getPlatforms(releaseDates) {
// TODO: change to single query with only needed properties
return await Promise.all(releaseDates.map(
elem => Platform.findOne({ id: elem.platform })
));
}
// will asynchronously map your genre list into the appropriate ObjectId objects
async function getGenres(genres) {
// TODO: change to return only single properties in a single query
var genres = await Promise.all(genres.map(elem => Genre.findOne({ id: elem })));
return genres.map(result => mongoose.Types.ObjectId(result._id));
}
// asynchronous request handler (ALWAYS use a try/catch for this with express)
// not sure if current/future versions will allow for promise resulting
// handlers/errors
async function saveGameDetails(req,res) {
try {
// array destructured assignment, decomposes the array
// await will await the promise, and promise.all will take an array
// and wrap them into a single promise.
var [platforms, genres] = await Promise.all([
getPlatforms(body.release_dates),
getGenres(body.genres)
]);
//prepare to save!
var game = {
igdb_id : body.id,
name : body.name,
summary : body.summary,
storyline : body.description,
genres : genres,
platforms : platforms, // <- genres amd platforms empty and not wait platforms and genre array to complete
release_date : body.original_release_date,
cover : body.cover.cloudinary_id,
videos: body.videos
};
var data = new Game(game);
await data.save(); //already a promise, just wait for it
// return normal result
res.status(200).json({ success: true });
} catch(err) {
// generic error handler, may want to have this even more generic via express
res.status(500).json({
error: {
message: err.message || 'Unknown Server Error';
}
})
}
}