2

I am fresher in NodeJs and Javascript. Excuse me for trivial question. I am trying to read the file with the following piece of code and I want to read line by line and I want to populate to an array. I am able to print but I am not able to return the array with all the lines. I want to use NodeJs non-blocking api to read the file. Please help me know the problem in the following code.

public readFileLineByLineNonBlocking(fileNamePath: string): any {
        let allLines: string[] = [];

        const readLines = readline.createInterface({
            input: fs.createReadStream(fileNamePath),
            output: process.stdout,
            terminal: false
        });

        readLines.on('line', (line) => {
            allLines.push(line);
//            console.log(line); // Prints the line
        });

        // The following lines do not print
        allLines.forEach((line) => {
            console.log("IP Address: ", line);
        });

        return allLines;
    }

For testing, I write the following class. If you run the following class, you should see each line to be printed in the console.

import * as fs from "fs"; import readline from "readline";

class Test1 {

    public readFileLineByLineNonBlocking(fileNamePath: string): String[] {
        let allLines: string[] = [];

        const readLines = readline.createInterface({
            input: fs.createReadStream(fileNamePath),
            output: process.stdout,
            terminal: false
        });

        readLines.on('line', (line) => {
            allLines.push(line);
        }).on('close', () => {
            // allLines is fully populated here
            // this is where you can use the value
            allLines.forEach((line) => {
                // console.log("IP Address: ", line); // I do not want to print
            });
        }).on('error', err => {
            console.log(err);
        });

        return allLines;
    }

}

let test1 = new Test1();
const fileNamePath: string = "testData/ipaddress-50.txt";

let allLines: any = test1.readFileLineByLineNonBlocking(fileNamePath);
console.log("All Lines: ", allLines);// Coming as empty
Deba
  • 859
  • 5
  • 9
  • 21
  • Not a trivial question at all - what do you have for `fileNamePath` are certain your code can read in the file relative to where it executes? – Michael Nelles Apr 25 '20 at 08:56
  • Sir, I am able to read the file properly, in this case, I pass the filePath "testData/ip-50.txt". – Deba Apr 25 '20 at 08:58

1 Answers1

1

readLines.on('line', ...) is registering an event handler that will be called many times in the future, not immediately. And, it doesn't block waiting for all the lines to finish. It just registers the event handler and immediately moves on.

So, your code is attempting to use allLines BEFORE it has been populated.

You need to instead, register an event handler for the close event:

public readFileLineByLineNonBlocking(fileNamePath: string): any {
        let allLines: string[] = [];

        const readLines = readline.createInterface({
            input: fs.createReadStream(fileNamePath),
            output: process.stdout,
            terminal: false
        });

        readLines.on('line', (line) => {
            allLines.push(line);
        }).on('close', () => {
            // allLines is fully populated here
            // this is where you can use the value
            allLines.forEach((line) => {
                console.log("IP Address: ", line);
            });
        });
    }

And, this whole operation is asynchronous so you can't directly return the result. You need to use it in the close event handler, call a callback from there and pass it the result, wrap the whole thing in a promise or use a different mechanism for getting the lines such as an async iterator.


FYI, when I run this exact code in a file all by itself, it works just fine:

const fs = require('fs');
const readline = require('readline');

function readFileLineByLineNonBlocking(fileNamePath) {
        let allLines = [];

        const readLines = readline.createInterface({
            input: fs.createReadStream(fileNamePath),
            output: process.stdout,
            terminal: false
        });

        readLines.on('line', (line) => {
            allLines.push(line);
        }).on('close', () => {
            // allLines is fully populated here
            // this is where you can use the value
            allLines.forEach((line) => {
                console.log("IP Address: ", line);
            });
        }).on('error', err => {
            console.log(err);
        });
}

readFileLineByLineNonBlocking("file1.txt");

So, (responding to your comment that this isn't working) whatever is causing your error is either because your code doesn't look like this or the problem is caused by something else, not by this code.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • SIr, thanks for the quick response, but still I do not get the data in the array. I pasted the code but it is not working for me Sir. I have added my test code, request you to check. – Deba Apr 25 '20 at 09:03
  • Sir, please guide me. – Deba Apr 25 '20 at 09:09
  • @Deba - See what I added to the end of my answer. That EXACT code (I'm not using TypeScript so I converted to plain JS) put into a file by itself runs just fine in node node v12.13.1. So, this concept works just fine if implemented properly. I will need more specifics to go on that "not working" to be able to help further. – jfriend00 Apr 25 '20 at 09:11
  • Sir, I am checking it, I am using NodeJs version 10. – Deba Apr 25 '20 at 09:12
  • Sir, I have updated, I have given a complete standlone Typesscript class to run. But it is not printing, the array is coming as empty. – Deba Apr 25 '20 at 09:31
  • @Deba - As my answer says (please read it again), you CANNOT return `allLines` from your function. Your function returns LONG before there are any values in `allLines`. This is how asynchronous code works. The canonical answer here on stackoverflow for returning asynchronous values is this [How do I return the response from an asynchronous call](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call/14220323#14220323). Please read that. And, read the part of my answer again about how you can't return that value directly. – jfriend00 Apr 25 '20 at 09:35
  • Got it Sir, I did not have this idea. +1 vote from my side. – Deba Apr 25 '20 at 09:39
  • @Deba - If this answered your question, you can indicate that to the community by clicking the checkmark to the left of the answer. That will also earn you some reputation points for following the proper procedure here. – jfriend00 Apr 25 '20 at 10:19