5 votes

Comment renvoyer les résultats de la requête Mongoose sans rappel

J'ai une application structurée comme suit : index > routes > endpoints > core, où les endpoints traitent les requêtes pour envoyer une réponse et core accède à la base de données. Le but de diviser les contrôleurs en ces deux composants était en partie de faire fonctionner les choses après un changement de base de données et en partie de simplifier mon code. Le problème est que je ne peux obtenir les résultats d'une fonction de requête mongoose dans mon module via un rappel, ce qui ne simplifie pas du tout mon code.
Ce que je veux:

var user = coreModule.findUser({id:123}) //retourne l'utilisateur
var posts = coreModule.findPosts({author:123}) //retourne un tableau de messages
res.render("home", {user:user, posts:posts})

En faisant:

//exemple de module core
findUser: function(params){
   User.findOne(params, function(err,docs){
      if(err){
         return err
      }else{
         return docs
      }
   }
}

Mais au lieu de cela, je dois utiliser un amas de fonctions de rappel qui contredisent la moitié du but des modules core en premier lieu. J'ai vu que vous pouvez faire cela avec SQL et Knex, mais cela ne fonctionnera pas avec Mongoose. Si ce que j'essaie de faire est impossible, y a-t-il un substitut de mongoose qui serait recommandé dans ce cas?

6voto

Mikhail Points 318

Utilisez la syntaxe Async/Await :

const findUser = async function (params) { 
    try {  return await User.findOne(params)
    } catch(err) { console.log(err) }
}

const userSteve = findUser({firstName: Steve})

Chaque fois que vous avez besoin d'utiliser les informations d'une requête, utilisez le préfixe await dans une fonction async. Cela vous permettra d'utiliser la nature asynchrone d'une requête Mongoose dans un code synchrone qui a besoin du résultat de cette requête pour continuer.

Pour votre code :

coreModule.findUser  = async function (userId) { 
    return await User.findOne({id:_userId})
}

coreModule.findPosts = async function(authorId) {
    return await Posts.find({postedBy: authorId})
}

const foundUser = coreModule.findUser(123);
const foundPosts = coreModule.findPosts(123);
res.send({user: foundUser, posts: foundPosts}) 

Si vous souhaitez que les deux requêtes s'exécutent simultanément, vous pouvez utiliser Promise.all()

coreModule.findUser  =  function (userId) { 
    return User.findOne({id:_userId})
}

coreModule.findPosts = function(authorId) {
    return Posts.find({postedBy: authorId})
}

const [foundUser, foundPosts] = 
   await Promise.all(
         [coreModule.findUser(123), coreModule.findPosts(123)]
   ); 

res.send({user: foundUser, posts: foundPosts})

Si vous avez les deux requêtes situées à des EndPoints séparés dans votre API, vous pouvez également construire deux requêtes fetch visant ces deux endpoints et les exécuter simultanément côté client avec Promise.all()

J'espère que cela vous a aidé !

Éditer : J'ai modifié mon post avec cet exemple fonctionnel que j'ai testé dans mon API :

module.exports.test = async function(req, res, next) {
    const module1 = function(params) {
        return ShoppingCartModel.find({_id: params})
    }
    const module2 = function(params) {
        return User.find({_id: params})
    }
    const array = [module1('5a739dbc45424d2904faca5b'), module2('5a739dbc45524d2904faca5b')]
    const promise = await Promise.all(array)
    res.json(promise)
}

Quelques exemples de retours incorrects :

Incorrect :

const array = [ShoppingCartModel.find({}), ShoppingCartModel.find({})]
// console.log(array) = Constructeurs de requête Mongoose
const promise = Promise.all(array)
// console.log(promise) = Promise {  }

Correct :

const array = [ShoppingCartModel.find({}), ShoppingCartModel.find({})]
const promise = await Promise.all(array)

Incorrect :

// Retourner une promesse complète à partir de la requête Mongoose en utilisant .exec()
const array = [ShoppingCartModel.find({}).exec(), ShoppingCartModel.find({}).exec()]
// console.log(array) = [ Promise {  }, Promise {  } ]
const promise = Promise.all(array)
// console.log(promise) = Promise {  }

Correct :

const array = [ShoppingCartModel.find({}).exec(), ShoppingCartModel.find({}).exec()]    
const promise = await Promise.all(array)

Vous devez attendre le résultat de Promise.all, sinon votre fonction passera à travers les appels de fonction, enverra un objet JSON vide au front-end, et console.log des promesses en attente qui n'ont pas le temps de se résoudre

0voto

David Vicente Points 1922

En ce qui concerne l'appel à la base de données qui est une chose asynchrone, vous devez attendre la réponse de manière asynchrone. Cependant, les rappels ne sont pas la seule méthode. Vous pouvez utiliser des promesses, ce qui permet d'avoir un code plus propre. Cela ressemblait à ceci:

coreModule.findUser({id:123})
.then(user => {
    return coreModule.findPosts({author:123});
})
.then(posts => {
    res.render("home", {user:user, posts:posts});
});

Et la partie mongoose devrait répondre à une promesse au lieu d'utiliser des rappels, comme ceci:

//exemple de module de base
findUser: function(params){
   return User.findOne(params).lean(true).exec();
}

J'espère que cela vous aidera

0voto

Kishor Patil Points 211
const findBook = async (queryParams) => { 
    try {  
         return await Book.findOne(params)
    } catch(error) { 
          // gérer les erreurs
  }
}

const getBook = async (req, res) =>{
    try{
     const book = findBook(queryParams);
     // le code suivant se trouve ici
    }catch(error){
          // gérer les erreurs
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X