2016-11-22 124 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