web-dev-qa-db-fra.com

Faire que les requêtes mongoose.js s'exécutent de manière synchrone

J'ai deux collections de mangouste. Le premier stocke une liste de lieux, le second est la visite des lieux. Le code de mon noeud passe au travers et tente d’obtenir la liste des visites à chaque endroit et de créer une chaîne que je produis en tant que JSON. La première requête est terminée avant que la deuxième ne commence - existe-t-il un moyen de les faire fonctionner de manière synchrone?

19
AlexKinsella

Si vous utilisez node.js, vous devez utiliser https://github.com/caolan/async

lorsque vous devez récupérer des données de plusieurs collections, vous devez chaîner vos requêtes plusieurs fois.

Cela rendra votre code complexe et difficile à lire et pas de modularité. Utilisez async pour créer Modularity en utilisant mongodb et node.js

Exemple de code de mon projet:

var async = require('async');

var createGlobalGroup = function(socket, data) {
    async.waterfall(
    [
    /**
     * this function is required to pass data recieved from client
     * @param  {Function} callback To pass data recieved from client
     */

    function(callback) {
        callback(null, socket, data);
    },
    /**
     * Step 1: Verify User
     */
    verifyUser,
    /**
     * Step 2: Check User Access Rights And Roles
     */
    checkUserAccessRightsAndRoles,
    /**
     * Step 3: Create Project
     */
    createNewGlobalGroup], function(err, result) {
        /**
         * function to be called when all functions in async array has been called
         */
        console.log('project created ....')
    });
}
verifyUser = function(socket, data, callback) {
//do your query
    /**
     * call next function in series
     * provide sufficient input to next function
     */
    callback(null, socket, data, {
        "isValidUser": true,
    });
}

checkUserAccessRightsAndRoles = function(socket, data, asyncObj, callback) {
    //do your query
    if(condition) {
        callback(null, socket, data, {
            roles: result,
            "isValidUser": asyncObj.isValidUser,
            "userId": asyncObj.userId,
        });
    } else {
    //no call back
    }
}

var createNewGlobalGroup = function(socket, data, asyncObj, callback) {
//wanna stop then no callback
}
15
Harpreet Singh

Il n'y a pas d'API synchrone native pour les requêtes mongodb/mongoose (et vous n'en voudriez pas en pratique) Comme WiredPrarie le mentionne, vous devez chaîner les requêtes, la seconde commençant après la fin de la première et l'exécution d'un rappel. Voici un exemple:

function findVisits(placesQuery,callback){
    Places.find(placesQuery).exec(function(err,places){
        if (err || !places.length){
            console.log('there was a problem');
            callback(err, null);
        }else{
            var visitQuery = ... //however you want to filter places
            Visits.find(visitQuery).exec(function(err2,visits){
                if (err2 || !visits.length){
                    console.log('there was a problem');
                    callback(err2,null);
                }else{
                    callback(null, visits)
                }
            });
        }
    });
}
11
mr.freeze

Si vous utilisez Node 8.x, vous pouvez utiliser async/wait: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await

wait l'opérateur suspend l'exécution de la fonction asynchrone jusqu'à ce que la promesse soit résolue et renvoie la valeur. De cette façon, votre code aura l’air plus synchrone:

const query1 = MyModel.find({ name: /john/i }, null, { skip: 10 });
const result1 = await query1.exec();

const query2 = MyModel.find({ name: /john/i }, null, { skip: 100 });
const result2 = await query2.exec();

Les requêtes seront exécutées successivement.

7
vsenko

De nos jours, mangouste supporte les promesses, donc vous pouvez .then() vos requêtes. Par exemple:

app.get('/notifications', function (req, res, next) {
  Users.findOne({
    username: req.body.username,
    password: req.body.password,
  }).then(user => {
    if (!user) {
      res.json({success: false, message: "Username or password incorrect."});
      return;
    }

    return Notifications.find({
      user: user._id
    }).then(notifications => {
      res.json({success: true, notifications});
    });
  ).catch(error => {
    //console.error(error);
    //res.json({success: false, error: error.message});
    next(error);
  });
});

Alternativement, maintenant que Javascript a async-wait, vous pouvez l'utiliser pour économiser quelques lignes et aplatir un peu le code:

app.get('/notifications', async (req, res, next) => {
  try {
    const user = await Users.findOne({
      username: req.body.username,
      password: req.body.password,
    });
    if (!user) {
      res.json({success: false, message: "Username or password incorrect."});
      return;
    }

    const notifications = await Notifications.find({
      user: user._id
    });
    res.json({success: true, notifications});
  } catch (error) {
    //console.error(error);
    //res.json({success: false, error: error.message});
    next(error);
  }
});
5
joeytwiddle

Pour synchroniser j'ai utilisé es6-promise.

var Promise = require('es6-promise').Promise
  , mongoose = require('mongoose')
  , Schema = mongoose.Schema;

// define schemas and models.
var placeSchema = new Schema({
        name: { type: String },
        memo: { type: String }
    })
  , Places = mongoose.model('place', placeSchema)
  , visitSchema = new Schema({
        placeName: { type: String }, // foreign key for place.
        visitor: { type: String },
        comment: { type: String }
    })
  , Visits = mongoose.model('visit', visitSchema);

// query for visits by visitor and place.
function findVisitsWithPlace(visitor, place) {
    return new Promise(function (resolve, reject) {
        Visits.find({
            visitor: visitor,
            placeName: place.name
        }, function (error, visits) {
            if (error) {
                reject(error);
                return;
            }

            // build a result object you want.
            // ()
            resolve({
                place: place,
                visits: visits
            });
        });
    });
}

// functions for node route.
module.exports = {
    // - access to "GET /placevisits/?visitor=Visitor-1".
    get: function (request, response) {
        var visitor = request.query.visitor;

        // - to get the places...
        Places.find({}, function (error, places) {
            Promise.all(places.map(function (place) {
                // - run the child queries with parent object...
                return findVisitsWithPlace(visitor, place);
            })).then(function (placeAndVisits) {
                // - and get result.
                // placeAndVisits have still contain visits empty.
                // exclude them.
                var result = [];
                placeAndVisits.forEach(function (placeandvisit) {
                    if (placeandvisit.visits.length != 0) {
                        result.Push(placeandvisit);
                    }
                });
                response.json(result);
            });
        });
    }
};

et j'ai JSON comme suit.

[
    {
        "place": {
            "_id": "564e58a1dbed862155771d46",
            "name": "Place-A",
            "memo": "A memo for Place A."
        },
        "visits": [
            {
                "_id": "564e58cedbed862155771d49",
                "placeName": "Place-A",
                "visitor": "Visitor-1",
                "comment": "A comment for Place A by Visitor-1"
            },
            {
                "_id": "564e58dcdbed862155771d4a",
                "placeName": "Place-A",
                "visitor": "Visitor-1",
                "comment": "2nd visit. Again comment for Place A by Visitor-1"
            }
        ]
    },
    {
        "place": {
            "_id": "564e58afdbed862155771d47",
            "name": "Place-B",
            "memo": "A memo for Place B."
        },
        "visits": [
            {
                "_id": "564e58ebdbed862155771d4c",
                "placeName": "Place-B",
                "visitor": "Visitor-1",
                "comment": "A comment for Place B by Visitor-1"
            }
        ]
    }
]
3
Kohei Sugimura

Voici ce que j'ai fini par faire ce soir.

mongoose.createConnection("mongodb://localhost:27017/chemresearch")
.then(async db => {
    const collection = db.collection("chemical");
    const all = collection.find({});

    while(all.hasNext()) {
        let chemical = await all.next();
        await work(chemical);
    }
});

La méthode work renvoie simplement une promesse.

const work = (chemical) => new Promise((resolve, reject) => {
    const casNumber = chemical.casRegistryNumber;
    analysis(casNumber, (error) => {
        error ? reject(error) : resolve(casNumber);
    })
});
0
Proximo

Voici une autre méthode pour créer des requêtes pseudo-synchrones à l'aide de MongooseJS. L'idée ici est de créer une file d'attente de requêtes devant être exécutées. Créez ensuite une fonction appelée récursivement jusqu'à épuisement de la file d'attente. Une fois la file d'attente épuisée, la récursivité s'arrête et une réponse est renvoyée pour la demande d'origine. J'utilise Express Routes afin que tout ce code soit encapsulé dans mon gestionnaire d'itinéraire. Dans ce cas, un HTTP POST.

var express = require('express');
var router = express.Router();

//POST /auth/create
router.post('/create', function(req, res) {
    var queue = [
        {"schema": require('..\\models\\people.js'), "query": {username: req.body.username}},
        {"schema": require('..\\models\\members.js'), "query": {username: req.body.username}}
    ],
    retData = []; 

    var curTask = 0.


    function recurse()
    {   
        if(curTask < queue.length){
                var task = queue[curTask];
                task.schema.findOne(task.query, function(err, data){
                retData.Push(err || data);
                curTask++;
                recurse();
            })
        }else{
            res.json(retData);
        }

    }

    recurse();

});



module.exports = router;
0
Jeffrey A. Gochin