简体   繁体   中英

Express.js async issue

I have a problem with async nature of node js, I have a this function

var sendData = function(req, res) {
    var requestId = req.params.id;
    var dataForSend = [];
    database.collection('songs').find({player_id:new ObjectID(requestId)}).toArray(function(err, player) {

        _.each(songs , function(song){
            var songID = song._id;
            song[song] = song;

            functions.generateViewData(song,data,false, function(playerData, song) {
                var dataForSong = {
                    // some code here
                }
                dataForSend.push(dataForChart);
                console.log("Kako ovo ne radi leb ti jebem" + JSON.stringify(dataForChart));
            });
        });
        res.send(dataForSend);
    });

}

My problem is that application dont wait for each loop to finish and populate dataForSend array , and Im res.send(dataForSend) empty array every time , how to wait for this loop to finish and then send dataForSend array to client?

Initialize a counter to the number of songs you have. Than, you decrement it in the loop for each song worked. When you reach 0, you send the data.

var songsRemaining = songs.length;
_.each(songs , function(song){
     var songID = song._id;
     song[song] = song;

     functions.generateViewData(song,data,false, function(playerData, song) {
         var dataForSong = {
             // some code here
         }
         dataForSend.push(dataForChart);

         songsRemaining--;

         if (songsRemaining === 0) {
              res.send(dataForSend);         
         }
     });
});

You could also invert the logic, initialize the counter to 0, increment it and check when you reach number of songs. It's just a question of habits, I usually prefer the countdown logic for this kind of situation.

I would recommend looking into using promises. Then you could create an array of async promises as you iterate and then wait on all promises to complete prior to returning your 'send' data.

Q ( https://github.com/kriskowal/q ) is a great promises library.

Something like this perhaps...

var Q = require("Q");

var sendData = function(req, res) {
    var requestId = req.params.id;
    var dataForSend = [];
    database.collection('songs').find({player_id:new ObjectID(requestId)}).toArray(function(err, player) {
    var promises = [];

        _.each(songs , function(song){
            var songID = song._id;
            song[song] = song;

        var promise = Q.fcall(function() {

                functions.generateViewData(song,data,false, function(playerData, song) {
                    var dataForSong = {
                        // some code here
                    }
                    dataForSend.push(dataForChart);
                    console.log("Kako ovo ne radi leb ti jebem" + JSON.stringify(dataForChart));
                });
        });
        promises.push(promise);
        });

    Q.all(promises).then(function() {
            res.send(dataForSend);
    });
    });

}

It in fact waits for _.each loop to finish, it's just that your _.each loop finishes before all of your functions.generateViewData calls finish. Because functions.generateViewData is async, _.each iterations return before functions.generateViewData calls the callback function.

I suggest you use asyncjs , it has an async.each function perfect for your use-case. You could do it like:

var sendData = function(req, res) {
    var requestId = req.params.id;
    var dataForSend = [];
    database.collection('songs').find({player_id:new ObjectID(requestId)}).toArray(function(err, player) {

        async.each(songs, 
           function(song,callback){
               functions.generateViewData(song,data,false, function(playerData, song) {
                    var dataForSong = {
                        // some code here
                    }
                    dataForSend.push(dataForChart);
                    console.log("Kako ovo ne radi leb ti jebem" + JSON.stringify(dataForChart));
                    //you call the callback with no parameters or null if no error occurs
                    callback(); 
               });
           }, 
           function(err){
               //since you never called callback with an error err will always be null and this function will be called when all your songs finished processing
               res.send(dataForSend);
           }
        );
    });
}

you could also use other asyncjs functions to tidy it up a little more

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM