1

I am trying to go through nodeschool's learnyounode.

This problem is the same as the previous problem (HTTP COLLECT) in that you need to use http.get(). However, this time you will be provided with three URLs as the first three command-line arguments.

You must collect the complete content provided to you by each of the URLs and print it to the console (stdout). You don't need to print out the length, just the data as a String; one line per URL. The catch is that you must print them out in the same order as the URLs are provided to you as command-line arguments.

Here is my code:

var bl = require('bl');
var http = require('http');

var urls = [process.argv[2], process.argv[3], process.argv[4]]
var dataArray = [];
var count = 0;


for (var i = 0; i <= urls.length-1; i++) {
  http.get(urls[i], function(response){ 
    response.setEncoding('utf8').pipe(bl(function (err, data) { 
      dataArray[i] = data.toString();
      count++;
      if (count==2){
        dataArray.forEach(function(item){
          console.log(item);
        })
      }
    }));
  });

};

And when I try to verify it:

  1. ACTUAL: "She'll be right gyno mate flat out like a milk bar. Grab us a gutta also mad as a battler. "
  2. EXPECTED: "Lets get some parma to watch out for the pav. She'll be right slabs no worries he's got a massive cracker. Lets get some gobful flamin she'll be right thongs. "

  3. ACTUAL: ""

  4. EXPECTED: "Get a dog up ya ute with it'll be sickie. He's got a massive yobbo bloody as cross as a bonza. "

  5. ACTUAL:

  6. EXPECTED: "She'll be right gyno mate flat out like a milk bar. Grab us a gutta also mad as a battler. "

  7. ACTUAL:

  8. EXPECTED: ""

What am I doing wrong here?

notthehoff
  • 1,172
  • 1
  • 12
  • 28
  • 1
    Are you including "http://" on the urls? – jmunsch Jan 03 '16 at 04:03
  • 2
    Possible duplicate of [JavaScript closure inside loops – simple practical example](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – MinusFour Jan 03 '16 at 04:16

2 Answers2

1

The script that you have written ends on the last argument passed in on the command line.

Using node 4.2.4 in debug I ran:

node debug test.js google.com facebook.com yahoo.com

Use c to continue.

repl to access the context while debugging.

s to step into a function.

And the loop ended with "yahoo.com" each time.

For loops are asynchronous using node, and as suggested in the duplication link you would need to bind or create a closure on the i counter. However, with a change in how the loop is being iterated:

var bl = require('bl');
var http = require('http');

var urls = [process.argv[2], process.argv[3], process.argv[4]]
var dataArray = [];
var count = 0;

urls.forEach(function(url, i){
  console.log(url)
  var options = {
    hostname: url,
    port: 80
  }
  http.get(options, function(response){
    console.log(options, response.statusCode)
    response.setEncoding('utf8').pipe(bl(function (err, data) {
      dataArray[i] = data.toString();
      count++;
      console.log(options, data)
    }));
  })  
  .on('error', function(e) {
    console.log("Got error: " + e.message);
  });
})

The result:

< google.com
< facebook.com
< yahoo.com
< { hostname: 'google.com', port: 80 } 301
< { hostname: 'google.com', port: 80 } <Buffer 3c 48 54 4d 4c 3e 3c 48 45 41 44 3e 3c 6d 65 74 61 20 68 74 74 70 2d 65 71 75 69 76 3d 22 63 6f 6e 74 65 6e 74 2d 74 79 70 65 22 20 63 6f 6e 74 65 6e ... >
< { hostname: 'yahoo.com', port: 80 } 301
< { hostname: 'yahoo.com', port: 80 } <Buffer 3c 48 54 4d 4c 3e 0a 3c 48 45 41 44 3e 0a 3c 54 49 54 4c 45 3e 45 72 72 6f 72 3c 2f 54 49 54 4c 45 3e 0a 3c 2f 48 45 41 44 3e 0a 0a 3c 42 4f 44 59 20 ... >
< { hostname: 'facebook.com', port: 80 } 302
< { hostname: 'facebook.com', port: 80 } <Buffer >

Note I added options since without including "http://" as part of the url I got:

< google.com
< facebook.com
< yahoo.com
< Got error: connect ECONNREFUSED 127.0.0.1:80
< Got error: connect ECONNREFUSED 127.0.0.1:80
< Got error: connect ECONNREFUSED 127.0.0.1:80

Please note:

Or using promises:

var bl = require('bl');
var http = require('http');

var urls = [process.argv[2], process.argv[3], process.argv[4]]
var dataArray = [];
var count = 0;

for (var i = 0; i <= urls.length-1; i++) {
  console.log(i)
  // Or you can use a promise which will create a closure with
  // the callback passed in

  // uncomment this promise to see the difference
  // Promise.resolve(i).then(function(i){
    console.log(i, urls)
    http.get({hostname: urls[i], port:80}, function(response){ 
      response.setEncoding('utf8').pipe(bl(function (err, data) { 
          dataArray[i] = data.toString();
          count++;
          console.log(urls[i], i, data)
      }));
    }).on('error', function(e){
      console.log(e.message)
    });
  // });
};

without promise:

< 0
< 0 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< 1
< 1 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< 2
< 2 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< undefined 3 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >
< undefined 3 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >
< undefined 3 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >

with promise:

< 0
< 1
< 2
< 0 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< 1 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< 2 [ 'http://google.com',
<   'http://facebook.com',
<   'http://yahoo.com' ]
< http://google.com 0 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >
< http://facebook.com 1 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >
< http://yahoo.com 2 <Buffer 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 69 74 6c 65 3e 34 30 30 20 42 61 64 20 52 65 71 75 65 73 74 3c 2f 74 69 74 6c 65 3e 3c 2f 68 65 61 64 ... >
Community
  • 1
  • 1
jmunsch
  • 22,771
  • 11
  • 93
  • 114
1

You want to check if count == 3 because you have 3 urls.

Also you don't want to use a function within a loop like that. In this case the value of i is 3 every time your anonymous function is executed ... That's why you're only outputting the expected value of the third url.

This is a great example of whats going on: JavaScript closure inside loops – simple practical example

By wrapping the function in a new function you can make sure that i is exactly what you want it to be.

This should work:

var bl = require('bl');
var http = require('http');

var urls = [process.argv[2], process.argv[3], process.argv[4]];
var dataArray = [];
var count = 0;


function juggle (i) {
  http.get(urls[i], function(response) {
    response.setEncoding('utf8').pipe(bl(function(err, data) {
      dataArray[i] = data.toString();
      count++;
      if (count == 3) {
        dataArray.forEach(function(item) {
          console.log(item);
        });
      }
    }));
  });
}

for (var i = 0; i < 3; i++) {
  juggle(i)
}
Community
  • 1
  • 1
bengrin
  • 26
  • 1
  • 2
  • So, am I right in saying that the for loop executes quickly enough and before the get function returns anything, that the i variable is at a 3? – notthehoff Jan 04 '16 at 15:41
  • 1
    yes, by the time the get function executes, the loop has finished running and i is at 3. – bengrin Jan 04 '16 at 19:28