Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NodeJS CSVReader - createReadStream with csv-parse returns empty array

I have the CSV reader class that takes two parameters (filePath, and model). The file path is the path to the .csv file, and the model is the model that the .csv will be built on. The data will be stored in the output array. It as has a problem as when I return the array, it is empty. But when I console log the array, it has the data in there. Can someone help me?

Index.js

const parse = require('csv-parse')
const fs = require('fs');
const output = [];

class CSVReader{
    static GetRecord(filePath, model){
        fs.createReadStream(filePath)
            .pipe(parse({columns: false, delimiter: ',', trim: true, skip_empty_lines: true}))
            .on('readable', function (){
                let record
                while (record = this.read()){
                    let city = model.create(record)
                    output.push(record)
                }
            })
            .on('end', function (){
                //console.log(output);
            })
        return output;
    }
}
module.exports = CSVReader;

I used Jest to test the file but it has a problem as expected is 6, but I received []

Index.test.js

const CSVReader = require('../src/Index');
const City = require('../src/Models/City')
test('Can Read CSV File', () => {
    let filePath  = 'data/worldcities.csv';
    let records = CSVReader.GetRecord(filePath, City);
    expect(records.length).toBe(6);
});
like image 904
BeefNoodle Avatar asked Nov 22 '25 19:11

BeefNoodle


1 Answers

tl;dr:

This is an async call, so you cannot just return the response and expect it to work. You need to use the Promise API and an async function.

What makes this async?

All of the Node.js fs API is async (excluding the fs.*Sync functions).

How can I use Promises?

You can return a Promise at the top of your function, then pass a callback:

return new Promise((resolve, reject) => { /* callback */ });

Fixing the code

// all of this is fine
const parse = require('csv-parse')
const fs = require('fs');
// remove the const output = []; as this will cause problems

class CSVReader{
    // this needs to be async
    static async GetRecord(filePath, model){
        // return a promise
        return new Promise((resolve, reject) => {
            // assign output here (https://stackoverflow.com/a/66402114/14133230)
            const output = [];
            fs.createReadStream(filePath)
                .pipe(parse({columns: false, delimiter: ',', trim: true, skip_empty_lines: true}))
                .on('readable', function (){
                    let record
                    while (record = this.read()){
                        let city = model.create(record)
                        output.push(record)
                    }
                    // you may need to call WriteStream#close() here
                })
                .on('end', function (){
                    // output is available here
                    // resolve the promise
                    resolve(output);
                })
        });
    }
}
module.exports = CSVReader;

Using the new Promise-based function

const CSVReader = require('../src/Index');
const City = require('../src/Models/City')
test('Can Read CSV File', () => {
    let filePath  = 'data/worldcities.csv';
    let records = CSVReader.GetRecord(filePath, City); // type Promise
    records.then((response) => { // when promise fills
        expect(response.length).toBe(6);
    });
});
like image 131
0xLogN Avatar answered Nov 24 '25 10:11

0xLogN



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!