-1

This is from the learnyounode tutorial exercise 9 on node.js. I'm having trouble understanding why my code doesn't print out the data in order.

let http = require('http'),
    bl = require('bl'),
    urlArray = [process.argv[2], process.argv[3], process.argv[4]]
    results = []
    //counter = 0;

function collectData(i) {
    http.get(urlArray[i], (res) => {
        res.pipe(bl((err, data) => {
            if (err) {
                return console.log(err);
            }
            data = data.toString();
            results[i] = data;
            //counter++;

            //if (counter === 3) {
              if (results.length === 3) {
                results.forEach((result) => {
                    console.log(result);
                })
            }
        }))
    })
}

for (let i = 0; i < urlArray.length; i++) {
    collectData(i);
}

The for loop should start from the first url and go through to the last in order. From my understanding, whatever happens in the current iteration of the loop must resolve for the loop to move to the next iteration. However, the results seem to be random. If I run my solution on the command line, sometimes the results are in order and sometimes they're not.

Edit: This is my current solution which works. I added the counter variable and put the http request into a function.

James
  • 127
  • 10
  • *"whatever happens in the current iteration of the loop must resolve for the loop to move to the next iteration"* – no, that is specifically what asynchronous code does *not* do. That is the very *definition* of asynchronous. – JJJ Feb 05 '17 at 14:18
  • @JJJ Does this mean that all for loops in node.js don't necessarily run in order? – James Feb 05 '17 at 14:22
  • No, it means that asynchronous methods don't necessarily resolve in order. The HTTP requests are sent in order, the callbacks are called whenever they get a response. – JJJ Feb 05 '17 at 14:23
  • That's sort of my question here. Does the callback function in each loop not need to finish in order for the loop to continue? Sorry, I think I get the concept of async but can't follow it in code. – James Feb 05 '17 at 14:28
  • I don't think you get the concept... the whole concept is that it doesn't wait for the methods to finish before continuing. If it did that then it wouldn't be asynchronous, it would be just normal synchronous code. I suggest reading [Asynchronous vs synchronous execution, what does it really mean?](http://stackoverflow.com/questions/748175/asynchronous-vs-synchronous-execution-what-does-it-really-mean) – JJJ Feb 05 '17 at 14:30

2 Answers2

0

The reason you're getting different results on each run is because the get-function of http is implemented asynchronously (async). You're doing the requests in the right order, but the webserver on the get-URL responds not instantly.

So basically, if you have two URLs to call:

http://stackoverflow.com

http://google.com

You call them in this order, but google have a good response time this run, like 10ms, stackoverflow needs a little bit longer like 20ms, your callback function for google is called at first, then the callback-function for stackoverflow.

The response times can be different each run, thats why you experience different results each run.

This is your callback-function:

res.pipe(bl((err, data) => {
    if (err) {
        return console.log(err);
    }
    data = data.toString();
    console.log(data);
}
Merschi
  • 56
  • 1
  • 5
  • I thought that since it was in a for loop, it wouldn't matter. From JJJ's comment and your answer, it seems that the program would have to know when everything is finished. This is what I did but it still doesn't seem to be working. results[i] = data; if (results.length === 3) { for (let j = 0; j < results.length; j++) { console.log(results[j]); } } I've added an if statement into the callback to see if the results array was filled. If so, then it would print out all the entries. – James Feb 05 '17 at 14:53
  • Can you please post it how you've done it now? Because i don't know how you handle over the iteration variable (i) to the callback-function – Merschi Feb 05 '17 at 15:40
  • Sorry for the delay. Updated. – James Feb 06 '17 at 12:27
  • I think in your version i is 0 every time. You define i outside of the callback-function and when the callback-function is evaluated, the first i is not in the scope anymore. You've to define a global variable for i and results or hand over the i AND the results to the callback-function. – Merschi Feb 06 '17 at 12:48
0

The entire problem is with the variable "i" and the asynchronous calls. With this particular logic, you don't have control over the value of i because of the async calls.

In order to understand the problem with your code, print console.log after the line: results[i] = data;

This is my solution to the problem:

var http = require('http');

var count =3;
var contentResults = [];


function hitRequests(i) {
     http.get(process.argv[i+1],function(response){
        response.setEncoding('utf8');

        var entireContent='';

        response.on('data', function(chunk){
            entireContent += chunk;
        });

        response.on('end', function(chunk){ 

            contentResults[i] = entireContent;
            count --;

            if(count <= 0)  {
                printAll();
            }   

        });  

    }).on('error',function(e){

        console.log('error'+e);
    });
}

for(i=1;i<=3;i++)    {
    hitRequests(i);
}

function printAll() {
    contentResults.forEach(function(result){
        console.log(result);
    });
}
  • @james Try to understand how the variable i behaves in iteration. But you won't have the same problem when you call a different function – Pankaj kumar Panigrahi Feb 06 '17 at 13:03
  • I edited my solution above. I do have another question though. If I don't use a counter and just have the if (results.length === 3) condition, it doesn't work out. With counters, you increase/decrease after the data has been added to the results array. Wouldn't it work the same if I'm comparing the results array length? I am just not getting something? – James Feb 06 '17 at 13:30
  • can you post your updated code so that i can have more clarity. – Pankaj kumar Panigrahi Feb 06 '17 at 18:19
  • Sorry for taking so long; had a busy week. I updated my code and commented out the counter system and put in the code in question. Instead of using a counter, I'm directly checking the length of the results array. – James Feb 12 '17 at 13:22