web-dev-qa-db-fra.com

Comment renvoyer les résultats d'une requête Mongoose sans rappel

J'ai une application structurée: index> routes> noeuds finaux> cœur, où les points finaux traitent les demandes d'envoi d'une réponse et où le cœur accède à la base de données. L'intérêt de diviser les contrôleurs entre ces deux composants était partiellement 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 recherche de mangouste 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}) //return user
var posts = coreModule.findPosts({author:123}) //return posts array
res.render("home", {user:user, posts:posts})

En faisant:

//core module example
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 fouillis de fonctions de rappel qui vont à l’encontre de la moitié de la fonction des modules de base. J'ai vu que vous pouvez le faire avec SQL et Knex, mais cela ne fonctionnera pas avec les mangoustes. Si ce que j'essaie de faire est impossible, existe-t-il un substitut de mangouste qui serait recommandé dans ce cas?

5
Isaac Krementsov

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 devez 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 du code synchrone nécessitant que le résultat de la requête se poursuive.

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 se déclenchent 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 vos deux requêtes sont situées à des points d'extrémité distincts dans votre API, vous pouvez également créer deux requêtes d'extraction destinées aux deux ordinateurs d'extrémité et les activer simultanément du côté client avec Promise.all ()

J'espère que cela a aidé!

Edit: J'ai édité mon message avec cet exemple de travail 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) = Mongoose Query Constructors
const promise = Promise.all(array)
// console.log(promise) = Promise { <pending> }

Correct: 

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

Incorrect:

// Return full fledged promise from Mongoose query using .exec()
const array = [ShoppingCartModel.find({}).exec(), ShoppingCartModel.find({}).exec()]
// console.log(array) = [ Promise { <pending> }, Promise { <pending> } ]
const promise = Promise.all(array)
// console.log(promise) = Promise { <pending> }

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 explose les appels de fonction, renvoie un objet JSON vide au serveur frontal et console.log des promesses en attente qu'il n'est pas temps de résoudre

6
Mikhail
const findBook = async (queryParams) => { 
    try {  
         return await Book.findOne(params)
    } catch(error) { 
          // handle the errors
  }
}

const getBook = async (req, res) =>{
    try{
     const book = findBook(queryParams);
     // next code goes here
    }catch(error){
          // handle the errors
    }
}
0
Kishor Patil

Dans la mesure où appeler une base de données est asynchrone, vous devez attendre la réponse de manière asynchrone. Mais, les callbacks ne sont pas le seul moyen… .. Vous pouvez utiliser des promesses, avec un code plus propre… .. Il 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 mangouste devrait répondre à une promesse utilisant des callbacks, comme ceci:

//core module example
findUser: function(params){
   return User.findOne(params).lean(true).exec();
}

J'espère que ça aide

0
David Vicente