0

I am using the async module to execute a set of function in parallel. However i am using a return value of another function in the callback of on of these function. But the problem is, function returns as undefined as the callback does not wait for it to be evaluated.

function getGenresId(genres){
    var genresArr = parseCSV(genres);
    Genre.find({name: {$in: genresArr } }, function (err, genres) {
        var genresId = genres.map(function (genre) {
            return genre.id;
        });
        return genresId.toString();
    });

}

async.parallel({
    getGenres: function (callback) {
        if(checkempty(req.query.genres) === true){
            callback(null, getGenresId(req.query.genres)});
        }
        else{
            callback(null, '');
        }

    },
    getActor: function (outercallback) {
        if(checkempty(req.query.actors) === true) {
            //other code here
        }
        else{
            outercallback(null, '');
        }
    }
);

I do understand that the function getGenresId() contains a blocking call to the database and Node.js will process it asynchronously. But is there a way I could force the

callback(null, getGenresId(req.query.genres)});

to evaluate the getGenresId function and wait for it to return a value before proceeding. I could use async's series function here but is there a native/better way to do this ?

3
  • Have you looked into promises? Commented Mar 9, 2015 at 10:46
  • @webduvet I am a beginner in Node.js. Could you please elaborate on what you mean by promises here ? Commented Mar 9, 2015 at 10:51
  • It is a design pattern you need. Try google javascript promise - you will get a lot of tutorials, articles, blogs... it is worth reading especially if you are a beginner. Commented Mar 9, 2015 at 11:41

2 Answers 2

1

The problem is the following assumption:

I do understand that the function getGenresId() contains a blocking call to the database

...Because getGenresId is not blocking at all. It returns undefined immediately (line #9) and then eventually runs a query on your database. It doesn't block anything else from running, so it's non-blocking.

Could you change the database call to be blocking? Somewhat (async-await, generators), but don't bother, because you're nearly there with your existing code. It only needs a small change to work:

// This function has a new argument, one named 'callback'.
function getGenresId(genres, callback){
    var genresArr = parseCSV(genres);
    Genre.find({name: {$in: genresArr } }, function (err, genres) {
        if(err) {
            callback(err);
            return; // Stop processing the rest of the function.
        }
        var genresId = genres.map(function (genre) {
            return genre.id;
        });
        // Instead of the `return` statement, we call the callback.
        callback(null, genresId.toString());
    });

}

async.parallel({
    getGenres: function (callback) {
        if(checkempty(req.query.genres) === true){
            // Call the function by passing the list of 
            // genres & this function's callback.
            // Functions are ordinary values.
            getGenresId(req.query.genres, callback);
        }
        else{
            callback(null, '');
        }

    },
...

See how the callback (which is passed to the getGenresId function) is called only after the result of your database are in? That's the way to do it. The value of the return statement that you had (return genresId.toString();), because it was inside of a non-blocking & asynchronous function (the database call), was discarded.

Other options (promises, generators, etc) are valid approaches too, but there's nothing wrong with the async module that you're using already.

Sign up to request clarification or add additional context in comments.

Comments

1

Using Q for node.js, you can return a promise from your getGenresId function, wait for it to be resolved and then call callback once it's resolved:

var Q = require('q');
function getGenresId(genres){
    var deferred = Q.defer(); // creating the promise
    var genresArr = parseCSV(genres);
    Genre.find({name: {$in: genresArr } }, function (err, genres) {
        var genresId = genres.map(function (genre) {
            return genre.id;
        });
        deferred.resolve(genresId.toString()); // resolving the prmoise
    });   
    return deferred.promise; // returning the promise
}

async.parallel({
    getGenres: function (callback) {
        if(checkempty(req.query.genres) === true){
            //waiting for the promise
            getGenresId(req.query.genres).then(function(data) { 
                callback(null, data);
            });

        }
        else{
            callback(null, '');
        }
  ....

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.