2016-11-22 84 views
1

我怎样才能将mongoose保存到数据库,但是等待其他数据集首先加载? 平台和流派是空的,因为“保存”功能在平台和流派加载之前运行,请大家帮忙!Node.JS + Mongoose回调地狱

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); 
    } 

    }); 
} 

回答

3

这是一个体面的使用情况的承诺(这是一个很好的工具,使您能够轻松执行异步操作),而且可以帮助你在未来的一个。

与当前代码的问题是,findOne操作是异步的,并会在一段时间后完成。同时,下一行将开始执行。因此,当你到达在save状态,没有findOne的将已经完成,你会得到空数组,其实现承诺是QBluebird

两个流行的NodeJS库。 NodeJS的最新版本也实现了默认的Promise

以下是使用Bluebird的代码。您必须为涉及平台和流派中的findOne的每个数据库操作创建承诺。当所有这些完成后,您必须开始执行最终的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); 
     } 

    }); 

}) 
+0

猫鼬已如果你离开过回调函数返回的承诺。 – Tracker1

+0

谢谢你指出。只是想解释如何使用承诺,来自同步背景的人。 – hyades

+0

@hyades,不错,但是如果你有三个数组,那么你只需在“result”中混合platformPromises和genrePromises,如果有三个数组,那么“for”函数是什么样的? 例如,platformPromises,genrePromises,themePromises – tonywei

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

2

好了,第一关,猫鼬(至少任何最近的版本)已经支持承诺,如果你离开回调...第二,下面的例子使用承诺与异步功能相结合。这是Node 7+中的一个选项标志的后面,所以你应该使用babel来转发。

我把你的意见,你应该优化你的mongodb调用,但离开尽可能接近逻辑以上,希望这可以帮助你。

关键拿走的题是...

  • 使用承诺,不要害怕创建额外的功能,打破了逻辑
  • Promise.all可以用来等待并行行动完成
  • 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'; 
     } 
    }) 
    } 
} 
+0

我不是用巴贝尔,但还是谢谢你 – tonywei

+0

谢谢!学到了新东西 – hyades