3

I'm trying to get the headers of a csv file into an array and attempting to use the csv-parser to do so. Two things I can't figure out here:

1) Why is the console.log on the last line of the code firing before anything else? This also means the variable's value I'm attempting to log is undefined.

2) Why isn't my getFields function returning the newly filled array of headers? I can console.log the results array with all of its new elements from inside the function, but I can't actually return the array full of elements--it keeps returning an empty array.

Here's the code:

const express = require('express')
const app = express()
const port = 3000
const csv = require('csv-parser')  
const fs = require('fs')

function getFields (filePath) {
  let results = [];
  fs.createReadStream(filePath)
    .pipe(csv())
    .on('headers', (headers) => {
      let values = Object.values(headers);
      values.forEach(function(element) {
        results.push(element);
      });
      console.log('I am actually getting results here: ', results);
      return results;
   });
}

const omg = getFields(thisIsAFilePath)
console.log('This console.log is firing before anything else: ', omg)
alyslov
  • 95
  • 7

1 Answers1

0

Your code uses an asynchronous callback. That means, the event handler (the (headers) => {...} function) is executed when the event (in this case headers) is fired. This happens asynchronously while the remaining script continues.

That means what happens is the following:

  1. The function is declared
  2. The function is called, as it returns nothing (does not even habe a return statement), undefined is stored inside omg
  3. The console.log call is executed
  4. At some point in the future the event headers is triggered an will execute the function (headers) => { ... }

So if you need the results, you need to put the code inside the callback or call a function from the inside like this:

function handleHeaders(headers) {
  console.log(headers);
}

function getFields (filePath) {
  let results = [];
  fs.createReadStream(filePath)
    .pipe(csv())
    .on('headers', (headers) => {
      handleHeaders(headers);
   });
}

Alternative: Promise

Alternatively, you can use the Promise API, which will allow to write your code like this:

(async () => {
    function getFields(filePath) {
        return new Promise(resolve => {
            let results = [];
            fs.createReadStream(filePath)
                .pipe(csv())
                .on('headers', (headers) => {
                    let values = Object.values(headers);
                    values.forEach(function (element) {
                        results.push(element);
                    });
                    console.log('I am actually getting results here: ', results);
                    resolve(results);
                });
        });
    }

    const omg = await getFields(thisIsAFilePath)
    console.log('This console.log is firing before anything else: ', omg)
})();

Keep in mind, the code is also executed asynchronously, it is just hidden in the form of Promises.

Thomas Dondorf
  • 23,416
  • 6
  • 84
  • 105
  • 1
    I'm still not clear on how I can return the value of `omg` to the global execution context so it can be used elsewhere in the program. – alyslov May 09 '19 at 03:53
  • `omg` will always be `undefined`. But I added a code sample to my answer to show you how you can pass the `headers` array to a function. – Thomas Dondorf May 09 '19 at 17:47
  • This helped me with an Azure Function that defaults to `async` function but the body I was using csv-parse `on()` methods. Wrapping in the `Promise` anonymous function and invoking `resolve()` with the results worked! – gabe Jul 03 '23 at 23:41