0

I have the following code for opening up a file, reading it in line by line, and pushing each line to an array. I am able to console.log the line before I push it to the array, so I know it has a value as a line, at that stage. But I am then unable to console.log it after that as an element of the array. I figure this has something to do with scope, but I can't see why it doesn't work at all - It just logs as "undefined".

var fs = require ("fs");

var lines = [];

function readLines(input, func)
{
    var remaining = '';

    input.on('data', function(data)
    {
        remaining +=data;

        var index = remaining.indexOf('\n');

        while(index > -1)
        {
            var line = remaining.substring(0, index);
            remaining = remaining.substring(index + 1);

            func(line);

            index = remaining.indexOf('\n');
        }
    });

    input.on('end', function()
    {
        if(remaining.length > 0)
        {
            func(remaining);
        }
    });
}

function func(data)
{
    console.log ("line : " + data);
    lines.push(data);
}

var input = fs.createReadStream('testing.csv');
readLines(input, func);

console.log(lines[0]);
  • 2
    Your I/O operations are **asynchronous**. The call to `readLines()` will return before anything has actually been read from the file. Asynchronous code structure is fundamental to Node.js. – Pointy Oct 28 '15 at 18:01
  • 1
    We need a button for this response, really. – Dave Newton Oct 28 '15 at 18:03
  • 1
    Possible duplicate of [How to return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – Dave Newton Oct 28 '15 at 18:04

2 Answers2

2

You may want to check after the function has finished - it is asynchronous so your console.log(lines[0]) will execute before it's read the data in (Most probably)

try adding:

input.on('end', function(){
    if(remaining.length > 0)
    {
        func(remaining);
    }
    console.log(lines[0]);
}

inside readLines()

Matt Fellows
  • 6,512
  • 4
  • 35
  • 57
1

Well, it has to do with the async nature of javascript.

You are creating a read stream the

fs.createReadStream('testing.csv');

and then, you are listening on its events:

input.on('data', function(data)
input.on('end', function()

but, the stuff within those events are executed, when they are triggered.

In your code you have:

var input = fs.createReadStream('testing.csv');
readLines(input, func);

// You are trying to access the line at position zero, 
// when it might not been set yet, 
// since the events might have not been triggered yet.
console.log(lines[0]);

You will need to make sure you are using the lines once the readLines is done try to have the end listener set outside the readLines function like this:

var input = fs.createReadStream('testing.csv');
readLines(input, func);

//console.log(lines[0]);

input.on('end', function(){
    console.log(lines[0]);
});

Javascript is a different beast, it has its own idioms and paradigms on how to program for it, you might want to read best practices as to how to better structure your code to work with its asynchronous nature.

lebobbi
  • 2,147
  • 17
  • 20
  • Thanks - I have one other question - and then I am good... I have the items being pushed into the array on "end" now - but when I do a for loop on that array and console.log each item, it just prints 0,1,2 - rather than the strings - any idea why that might be? – MickeyThreeSheds Oct 28 '15 at 18:21
  • @MickeyThreeSheds you've probably written a `for ... in` loop (which is not a good idea) and in JavaScript that iterates through the property *names*, not the property values. – Pointy Oct 28 '15 at 18:24
  • I love you! This was exactly what I needed, and thanks for answering the extra question! – MickeyThreeSheds Oct 28 '15 at 18:33