Search code examples
javascriptasynchronousnode-mysql

Javascript: Asynchronous function confusion


I have a function which calls an asynchronous function (connection.query), I want to modify the parameter comment and store the modified value in variable res and return it. However the problem is that the return statement is being executed before the forEach finishes, inside of which there's an asynchronous function. How do I ensure that the two statements console.log("outside:" + res); and return res; happen ONLY after everything above it is done executing.

var res = "";


function testComment(comment) {
    var words = comment.split(" ");
    words.forEach(function(word, i){
        (function(index){
            connection.query('select good_words from words where bad_words = ' + connection.escape(word), function(err, result){
                if(err){
                    console.log(err);
                    return;
                }
                if(result.length != 0){
                    this.res = comment.replace(word, result[0].good_words);
                    console.log("inside:" + this.res);
                }
            });
        })(i);
    });
    console.log("outside:" + this.res);
    return this.res;
}

I tried doing this, I created a second function which returns the value res like so,

function callback(){
   return res;
}

and I modified testComment to accept a callback and then return it like this,

function testComment(comment, callback){
  ..all the working..(forEach loop which has a asyn function in it)
  return callback();
}

But here the problem is that testComment would return the callback() before forEach finishes, this is really confusing me. Any way to fix this?

EDIT : For more context, this is what the function does, I have a table in a database which has a list of bad_words and a list of good_words which replace these bad_words in the input string (comments in this case). The parameter comment is to be tested for the bad_words and replaced by the corresponding good_words.


Solution

  • You should use something like:

    var wordsProcessed = 0;
    (function(index){
        connection.query('select good_words from words where bad_words = ' + connection.escape(word), function(err, result){
            wordsProcessed++;
            if(err){                 
                console.log(err);
                return;
            }
            if(result.length != 0){
                this.res = comment.replace(word, result[0].good_words);
                console.log("inside:" + this.res);
            }
            if(wordsProcessed >= words.length){
                //THIS IS WHERE YOU RETURN YOUR RES
            }
        });
    })(i);
    

    Or more conveniently, you should use bluebird :)

    var Promise = require('bluebird');
    var promiseArray = words.map(function(word){
        return new Promise(function(resolve, reject){
            connection.query('select good_words from words where bad_words = ' + connection.escape(word), function(err, result){
                if(err) reject(err);
                else resolve(result);
            }); 
        });
    });
    
    Promise.all(promiseArray).then(function(result){
        return this.res;
    });