1

I am a beginner to node.js. I was trying out the examples from the 'learnyounode' tutorial. I am trying to write a program that takes three url parameters and fetches some data from those urls and displays the returned data in the order in which the urls were provided.

var http = require('http');
var bl = require('bl');
var url = [];
url[0] = process.argv[2];
url[1] = process.argv[3];
url[2] = process.argv[4];
var data = [];
var remaining = url.length;
for(var i = 0; i < url.length; i++){
    http.get(url[i], function (response){
        response.setEncoding('utf8');
        response.pipe(bl(function (err, chunk){
            if(err){
                console.log(err);
            }       
            else{
                data[i] = chunk.toString(); 
                console.log(data[i]);
                remaining -= 1;
                if(remaining == 0)  {
                    for(var j = 0; j < url.length; j++){
                        console.log(data[j]);
                    }                   
                }               
            }           
        }));        
    }); 
}

I have two console.log statements in the program. The output i get is as follows:

It'll be chunder where lets throw a ford. We're going durry where mad as a cooee
.
Shazza got us some apples with come a strides. Mad as a swag when get a dog up y
a roo. It'll be rapt piece of piss as cunning as a trackie dacks.
As cross as a bogged with watch out for the boardies. As cunning as a digger fla
min lets get some roo bar. As dry as a piker piece of piss he hasn't got a joey.
 Lets throw a strides mate we're going digger.
undefined
undefined
undefined

It seems like the data is correctly fetched and stored in the 'data' array but it still displays undefined. Any idea why this is happening? Thanks in advance!

adz
  • 759
  • 1
  • 5
  • 9

1 Answers1

1

This is a very common issue in async programming in node.js or even in the browser. A main issue you have is that the loop variable i will not be what you want it to be some time later when the async callback is called. By then, the for loop will have run to the end of its loop and i will be at the end value for all response callbacks.

There are numerous ways to solve this. You can use a closure to close over the i value and make it uniquely available to each callback.

var http = require('http');
var bl = require('bl');
var url = [];
url[0] = process.argv[2];
url[1] = process.argv[3];
url[2] = process.argv[4];
var data = [];
var remaining = url.length;
for(var i = 0; i < url.length; i++){
    // create closure here to uniquely capture the loop index
    // for each separate http request
    (function(index) {
        http.get(url[index], function (response){
            response.setEncoding('utf8');
            response.pipe(bl(function (err, chunk){
                if(err){
                    console.log(err);
                }       
                else{
                    data[index] = chunk.toString(); 
                    console.log(data[index]);
                    remaining -= 1;
                    if(remaining == 0)  {
                        for(var j = 0; j < url.length; j++){
                            console.log(data[j]);
                        }                   
                    }               
                }           
            }));        
        }); 
    })(i);
}

If you do much node.js programming, you will find that you probably want to learn how to use promises because they are very, very handy for controlling the flow and sequence of async operations.

jfriend00
  • 683,504
  • 96
  • 985
  • 979