Capturing value of an asynchronous function in NodeJS

3

Hello, I have a problem that I have not found a solution yet.

I have an array of values, in which I make a scan and for each value execute function that makes a request to dynamodb and returns a JSON. This request is asynchronous. With the value of each query, I want to populate an array, which will be returned via callback to my View .

The problem is that when I give a push in the Array, it returns me empty when I exit the scope of my asynchronous function.

exports.appCategory = function(ids, callback1){

    var result = { apps: []};

    async.forEachSeries(ids, function(item, cb){
            async.series([
                    function(callback){
                            //funcao assincrona
                            verify(item.id, function(res){
                                    result.apps.push(res);
                            });
                            callback();
                    },
            ]);
            cb();
    });

    console.log(result);
}

My database query function:

function verify(id, callback){

    scrapeDB.getItem(id, function(response){
            var category;
            url = "https://play.google.com/store/apps/details?id="+id+"&hl=pt";

            if(response === null){
                    request(url, function(error, response, html){

                            if(error){
                                    console.log(error);
                                    return false;
                            }else{

                                    var $ = cheerio.load(html);

                                    if(response.statusCode == "404"){
                                            category = "Outros";
                                    }else{
                                            $('.category').filter(function(){
                                                    var data = $(this);
                                                    dataCategory = data.children().first().text();
                                                    category  = dataCategory;
                                            });
                                   }//else

                                   //save data
                                   scrapeDB.putItem(id, category, function(resp){
                                            callback(resp);
                                            //return resp;
                                   });

                            }//else       
                    });//request
            }else{
                    callback(response);
                    //return response;
            }

    });//scrapedb

}//function verify()

My current result is:

{ apps: [] }

The expected result is somewhat similar to (a JSON Array):

{ apps: [{id: "value1", category: "category1"}, {id: "value1", category: "category1"}] }
    
asked by anonymous 27.01.2015 / 23:01

1 answer

2

You have several problems in your code but the basic problem is that you are assuming that the line after the asynchronous function call only runs after the server has responded. In fact, the opposite occurs.

For example, if you run

 requestDataFromServer(function(){
     console.log("servidor respondeu");
     array.push(...)
 });
 console.log("agora tudo acabou")

The result you're likely to get will be the opposite of what you expect:

agora tudo acabou
servidor respondeu

To solve this, what you should do is always pass a callback to the asynchronous functions and put the rest of your execution stream that depends on the result of the asynchronous call within that callback.

exports.appCategory = function(ids, callback1){

    var result = { apps: []};

    async.forEachSeries(ids, function(item, cb){
        async.series([
            function(callback){
                //funcao assincrona
                verify(item.id, function(res){
                    result.apps.push(res);
                    callback();
                });
            },
        ], function(){
            cb()
        });
    }, function(){
        console.log(result);
        callback1();
    });
}

In addition, there are some modifications that I think leave the code more beautiful in this case:

  • Using assinc.series with a list of size 1 is redundant
  • function(){ cb() } can be written as cb
  • I think the function's real name is eachSeries

that is:

exports.appCategory = function(ids, callback1){
    var result = { apps: []};
    async.eachSeries(ids, function(item, cb){
        verify(item.id, function(res){
            result.apps.push(res);
            cb()
        });
    }, function(){
        console.log(result);
        callback1();
    });
}

ps: I recommend you take a look at each (without the "series") function to make the code more fast and do not have to keep pushing.

    
28.01.2015 / 02:31