user3728233 user3728233 - 1 month ago 5
Javascript Question

Javascript: Asychronous function confusion

Okay, I have been stuck on this for over an hour now. So 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
.

Answer

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;
});