如何将mongoose保存到db但是等待其他集合首先加载? 平台和流派是空的,因为" save"功能在平台和流派加载之前运行,请帮助!
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);
}
});
}
答案 0 :(得分:3)
这是promises的一个不错的用例之一(这是一个很好的工具,使您能够轻松地执行异步操作),并将在未来帮助您。
当前代码的问题是findOne
操作是异步的,并且会在一段时间后完成。与此同时,下一行将开始执行。因此,当您达到save
状态时,findOne
都不会完成,并且您将获得空数组
实现承诺的两个流行的nodejs库是Q和Bluebird。最新版本的NodeJS还实现了默认的Promise
以下是使用Bluebird的代码。您必须为Platform和Genre中涉及findOne
的每个数据库操作创建promise。完成所有这些后,您必须开始执行最终的save
部分。这是使用Promise.all功能实现的,该功能将等待所有承诺完成。
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);
}
});
})
答案 1 :(得分:2)
好的,首先,mongoose(至少是任何最新版本)已经支持promises如果你不用回调...第二,下面的例子使用promises和异步函数。这是Node 7+中的选项标志的背后,所以你应该使用babel进行转换。
我把评论放在你应该优化你的mongodb调用的地方,但是让逻辑尽可能接近上面,希望这对你有帮助。
关键的收获是......
async
功能非常棒。CODE:
// 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';
}
})
}
}
答案 2 :(得分:1)
可以用来完成这项工作的是异步模块,它非常适合执行这类任务。 Intall使用npm:npm i -S async
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])
}
})
我没有运行此代码,但就是这样,如果您需要更多信息,可以阅读文档:http://caolan.github.io/async/docs.html