Неожиданное поведение при использовании модуля узла csv-parser

Я пытаюсь получить заголовки файла csv в массив и пытаюсь использовать csv-parser для этого. Две вещи, которые я не могу понять здесь:

1) Почему console.log в последней строке кода запускается раньше всего? Это также означает, что значение переменной, которую я пытаюсь записать, не определено.

2) Почему моя функция getFields не возвращает недавно заполненный массив заголовков? Я могу console.log массив результатов со всеми его новыми элементами изнутри функции, но на самом деле я не могу вернуть массив, полный элементов - он продолжает возвращать пустой массив.

Вот код:

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)

1 ответ

Ваш код использует асинхронный обратный вызов. Это означает, что обработчик события ((headers) => {...} функция) выполняется, когда происходит событие (в данном случае headers) уволен. Это происходит асинхронно, пока продолжается оставшийся сценарий.

Это означает, что происходит следующее:

  1. Функция объявлена
  2. Функция вызывается, так как ничего не возвращает (даже не имеет return заявление), undefined хранится внутри omg
  3. console.log вызов выполнен
  4. В какой-то момент в будущем событие headers запущен и выполнит функцию (headers) => { ... }

Поэтому, если вам нужны результаты, вам нужно поместить код в функцию обратного вызова или вызвать функцию изнутри следующим образом:

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

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

Альтернатива: обещание

В качестве альтернативы вы можете использовать API Promise, который позволит писать ваш код следующим образом:

(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)
})();

Имейте в виду, что код также выполняется асинхронно, он просто скрыт в форме обещаний.

Другие вопросы по тегам