Nodejs Mongoose – comment éviter l’enfer de rappel?

Je me trompe peut-être avec Mongoose après l’avoir utilisé pendant un moment. J’ai du mal à lire quand vous avez trop de rappels, par exemple:

Je veux trouver un document et le mettre à jour. Je dois d’abord le trouver, puis la update trouvera à l’intérieur du rappel de findOne .

 var sam = new Character({ name: 'Sam', inventory: {}}); Character.findOne({ name: 'Sam' }, function(err, character) { console.log(character); // now I want to update it. character.update({... }, function(err, characterID) { // now I want to know the size of the document after the update. Character.findOne({ _id: characterID }, function(err, character) { // Now update it again.... }); }); }); 

Il finit par un code spaghetti!

Tu vois ce que je veux dire?

Des idées comment mieux le faire?

Ou mongodb native est mieux sans tous ces rappels?

MODIFIER:

 Character.findOne({...}, function(err, character) { return character.update(...); }).select("-field1 -field2").then(function(data) { // }).catch(function(error) { // Handle any error from all above steps }).done(); 

Erreur:

 TypeError: Character.findOne(...).select(...).then(...).catch(...).done is not a function 

Vous pouvez utiliser le chaînage de q promise dans nodejs

 var Q = require('q'); function findOne(filter) { return Q.Promise(function(resolve, reject) { Character.findOne(filter, function(err, character) { resolve({ err: err, character: character }); }); }); } function update(data) { return Q.Promise(function(resolve, reject) { character.update(data, function(err, characterID) { resolve({ err: err, characterID: characterID }); }); }); } findOne({ name: 'Sam' }).then(function(data) { if (!data.err) { // now you can update it. return update(data.character); } else { throw new Error(data.err); } }).then(function(data) { if (!data.err) { // now you can update it. return update(data.characterId); } else { throw new Error(data.err); } return findOne({ id: characterId }); }).then(function(data) { if (!data.err) { // now you can update it. return update(data.character); } else { throw new Error(data.err); } }).catch(function(error) { // Handle any error from all above steps }).done(); 

Puisque Mongoose supporte les promesses (documentées ici ), vous pouvez réécrire votre code comme suit:

 var sam = new Character({ name: 'Sam', inventory: {}}); Character.findOne({ name: 'Sam' }).then(character => { return character.update(...); }).then(characterID => { return Character.findOne({ _id: characterID }); }).then(character => { ... }).catch(err => { // TODO: handle error. }); 

Il n’est pas nécessaire d’encapsuler chaque fonction que vous utilisez à l’aide d’une bibliothèque de promesses externe. Vous pouvez recevoir un avertissement sur Mongoose en utilisant une bibliothèque de promesses obsolète, qui est facilement corrigée en incluant ceci en haut de votre code:

 mongoose.Promise = global.Promise; 

Documenté ici .

Cependant, vous pouvez remplacer toute la chaîne de promesse ci-dessus par une seule commande:

 Character.findOneAndUpdate({ name : 'Sam' }, { $set : { inventory : {} } }, { new : true }).then(character => { ... }); 

Documenté ici .

Vous avez maintenant découvert “callback enfer” . Cela ne se limite pas à Mongoose ou Mongo, mais fait partie de toute la programmation de Node.js et ne vous sentez pas seul. Nous avons tous dû faire face à cela.

Dans MongoDB (ou tout événement de données avec la plupart des bases de données), le problème est la chaîne d’événements en cours et la nécessité de corriger les erreurs ou de faire attendre le programme pour que l’étape soit terminée.

  1. Les promesses peuvent être une solution asynchrone ou synchrone pour les rappels. Vous pouvez utiliser bluebird pour envelopper vos événements dans Promises. Cela nécessite une compréhension des constructions .then et return . Cependant, essentiellement, vous dites: “Faites l’étape 1, puis faites l’étape 2.” (etc). Rappelez-vous cependant que, selon le codage, vous devez toujours détecter les erreurs et que vous pouvez facilement synchroniser l’intégralité de votre code, ce qui n’est pas une situation idéale dans Node.js. Cependant, cela résoudra “l’enfer de rappel” des connexions Mongo. Ce que je dis, c’est d’essayer de ne pas prendre l’habitude de mettre tout votre code dans le paradigme .then -> return, mais uniquement les endroits où il est vraiment nécessaire.

  2. Le nouveau pilote de noeud MongoDB (version 2.2.5) est bien meilleur que le précédent. Mongo a instancié ECMAScript 6 si vous incluez le module co , qui fournit les événements de rendement et de retour (lisez ci-dessous à propos de async / wait, car il s’agit essentiellement de la même chose). C’est une lumière d’avance sur ce que le conducteur était jusqu’à présent. Cela vous aidera à sortir de “l’enfer du rappel” avec vos connexions Mongo et vos opérations CRUD.

Donc, pour sortir de “l’enfer du rappel”, la réponse normale est d’utiliser les promesses.

Pour rendre les choses un peu plus compliquées, le nouveau Node.js (pas le LTS) mais les versions supérieures à 7.7XX qui ne sont pas encore officielles utilisent une structure async / wait tant attendue de ES7. Cela devrait garder votre code asynchrone avec des promesses. Un article extérieur sur ceci peut être vu ici .

En fonction de votre application et de vos besoins, essayez d’utiliser Promises ou utilisez simplement le nouveau pilote MongoDB avec co pour Mongo. Quant au module à utiliser, que ce soit bluebird, co ou autre, c’est à vous de décider. Lorsque Node.js publie officiellement le fichier async / wait et qu’il fonctionne comme il est annoncé, il serait judicieux d’utiliser ce modèle au lieu de rappels.