2016-11-22 2 views
1

Wie kann ich Mungo in db speichern, aber warten andere Sammlung zuerst laden? Die Plattform und Genres ist leer, weil die "Speichern" -Funktion vor Plattform und Genres geladen, bitte helfen!Node.JS + Mongoose Rückruf-Hölle

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

    }); 
} 

Antwort

3

Dies ist eines des ein anständiger Anwendungsfalles für Versprechungen (die ein ausgezeichnetes Werkzeug sind so dass Sie async Operationen mit Leichtigkeit durchzuführen) und sollen Ihnen in Zukunft helfen.

Das Problem mit dem aktuellen Code ist, dass die findOne Operationen async sind und nach einiger Zeit abgeschlossen werden. In der Zwischenzeit beginnen die nächsten Zeilen mit der Ausführung. Wenn Sie also am save Zustand erreichen, keiner der findOne hätte abgeschlossen und Sie leere Arrays erhalten

Zwei beliebte NodeJS Bibliotheken, die Versprechungen sind Q und Bluebird implementieren. Neueste Versionen von NodeJS implementieren auch die Standardeinstellung Promise

Folgendes ist der Code mit Bluebird. Sie müssen im Grunde für jede Ihrer Datenbankoperationen Versprechungen erstellen, die findOne in Platform und Genre beinhalten. Wenn all dies abgeschlossen ist, müssen Sie mit der Ausführung des letzten save Teils beginnen. Dies wird erreicht mit der Promise.all Funktionalität, die warten würde, bis alle Versprechen abgeschlossen sind.

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

Mongoose bereits wieder verspricht, wenn Sie die Callback-Funktion weglassen. – Tracker1

+0

Vielen Dank, dass Sie darauf hingewiesen haben. Ich wollte nur erklären, wie man Versprechungen für jemanden verwendet, der aus einem synchronen Hintergrund kommt. – hyades

+0

@hyades, schön, aber was ist, wenn Sie drei Array zu bekommen haben, mischen Sie einfach platformPromises und genrePromises in "result", wie sieht die "for" -Funktion aus, wenn es drei Arrays gibt? Beispiel platformPromises, genrePromises, themePromises – tonywei

1

Was Sie verwenden können, um diese Arbeit zu tun, ist Async-Modul, das perfekt für diese Art von Aufgaben ist. Intall mit 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]) 
       } 
      }) 

ich diesen Code nicht ausgeführt, aber das ist die Art und Weise, wenn Sie weitere Informationen benötigen, können Sie die Dokumentation lesen: http://caolan.github.io/async/docs.html

2

Okay, zunächst einmal, Mungo (zumindest Jede neuere Version unterstützt bereits Versprechungen, wenn Sie den Rückruf nicht ausführen ... Zweitens verwendet das folgende Beispiel Versprechen, die mit asynchronen Funktionen kombiniert werden. Dies steht hinter einem Options-Flag in Node 7+, Sie sollten also babel verwenden, um anderweitig zu transpilieren.

Ich habe Kommentare, wo Sie Ihre mongodb Anrufe optimieren sollten, aber die Logik so nahe wie möglich oben, hoffentlich dies hilft Ihnen.

Die wichtigsten Imbissbuden sind ...

  • Verwenden Promises, und haben Sie keine Angst um zusätzliche Funktionen zu erstellen Logik zu brechen
  • Promise.all verwendet werden kann, für die parallelen Aktionen warten komplett
  • async Funktionen sind genial.

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

im nicht babel verwenden, aber trotzdem danke – tonywei

+0

Dank! etwas Neues gelernt – hyades

Verwandte Themen