-3

I am practising node.js and i have some doubt.

I have a csv file (users.csv) and i have created a node module (library.js) to return the values in the csv file. When i call the module from my program i am getting weired results. My program is given below.

users.csv

id,name
1,Jim
2,Jo

library.js

var csv             = require("fast-csv");
var DataToBeReturned = [];
exports.users = function () {
        csv.fromPath("users.csv", {headers: true})
        .on("data",function(data){
            DataToBeReturned.push(data);
        })
        .on("end",function(){   
            console.log(DataToBeReturned.length);
            return DataToBeReturned;
        });
};

app.js

var mylibrary = require('./library.js');
console.log(mylibrary.users());
console.log(mylibrary.users());

my expected output is

2
[{ id: '1', name: 'Jim' },{ id: '2', name: 'Jo' }]
2
[{ id: '1', name: 'Jim' },{ id: '2', name: 'Jo' }]

instead i am getting

undefined
undefined
3
4

My doubts :

  1. Why returned value is undefined?
  2. Why my count is not 2 always?

Thanks in advance.

Sriram
  • 767
  • 2
  • 17
  • 42
  • Possible duplicate of [How do I return the response from an asynchronous call?](http://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – Ben Fortune Dec 18 '15 at 09:54
  • @BenFortune, that's a good catch, Felix's answer to that question goes into a lot more detail about async programming. – hargasinski Dec 18 '15 at 09:59

1 Answers1

1

I haven't been able to test your code myself, but from reading your code, you are running into two problems, which go hand in hand with your questions.

Firstly, you are running into the typical problem most people run into when starting out with nodejs. You are calling an asynchronous function, synchronously. That's why it's printing undefined the first two times, you are printing the results of calling users. Since users hasn't finished running yet, it's value is undefined. You should implement the library.js function users with a callback. For example:

exports.users = function (callback) {
    csv.fromPath("users.csv", {headers: true})
    .on("data",function(data){
        DataToBeReturned.push(data);
    })
    .on("end",function(){   
        console.log(DataToBeReturned.length);
        return callback(null, DataToBeReturned);
    });
};

Then in app.js you would implement this to work with callbacks.

var csv             = require("fast-csv");
// err is just a nodejs convention, if you add error checking to users,
// instead of throwing an error, you would pass it back as the first agrument
// to the callback
mylibrary.users(function(err, users) {
    console.log(users);
});
mylibrary.users(function(err, users) {
    console.log(users);
});

Secondly, you are referring to a global variable from your users function. That is you print out 3 and 4. Both calls are acting on the variable at once. When the first call finishes and returns the value, the second call had already also modified the array. So, you want to move the array declaration instead of your function, so only code in there has access to it.

exports.users = function () {
    var DataToBeReturned = [];
    // the rest of your code.
};

EDIT: the code inside of modules is cached, so it is only executed once. Then each script that refers to it is simply returned that copy. If you need to only read the csv once, use this to your advantage.

var DataToBeReturned = [];
var called = false;
function read(callback) {
    if (called) {
        process.nextTick(function() {
            return callback(null, DataToBeReturned);
        });
        return;
    } else {
        called = true;
        csv.fromPath("users.csv", {headers: true})
        .on("data",function(data){
            DataToBeReturned.push(data);
        })
        .on("end",function(){   
            console.log(DataToBeReturned.length);
            return callback(null, DataToBeReturned);
        });
    }
};

exports.users = function(callback) {
    return read(callback);
};

The process.nextTick part is important, more information on how it works can be found here in the documentation.

hargasinski
  • 841
  • 5
  • 14
  • Thanks @Zequ. With this solution i am getting correct answer. But again i am facing another issue. Everytime when i call the method it is reading file. Instead i need to read file only once – Sriram Dec 18 '15 at 10:08