Node.js - 스트림 및 약속

저는 로컬 파일 시스템에서 큰.csv 파일을 읽은 다음 데이터로 작업해야 하는 프로젝트를 진행해 왔습니다. Node.js에는 스트림, 이벤트 이미터, readline 네이티브 모듈과 같은 작업을 위한 훌륭한 도구가 있습니다. 그러나 모든 예제 코드/튜토리얼은 다음 세 가지 범주 중 하나에 속했습니다.
  • 데이터를 콘솔에 인쇄(유용하지 않음)
  • 파일에 데이터 쓰기
  • 들어오는 데이터를 외부 어레이로 푸시합니다
  • .
  • 외부 라이브러리 사용

  • 외부 라이브러리csv-parser를 사용하여 시작했습니다. 그러나 이것은 기본적으로 위에서 나열한 기본 Node.js 기술을 둘러싼 래퍼이므로 아래에 나열할 데이터로 작업하는 것과 동일한 문제가 있습니다. 나는 결국 그것을 제거하고 나만의 경량 버전을 작성했습니다.

    배경



    The `readline` module provides an interface for reading data from a Readable stream...one line at a time. from [Node.js Documentaion](https://nodejs.org/docs/latest-v16.x/api/readline.html)


    All streams are instances of EventEmitter. from [Node.js Documentaion](https://nodejs.org/docs/latest-v16.x/api/stream.html)


    기본적으로 스트림으로 작업한다는 것은 데이터로 이벤트를 수신한다는 의미입니다. 그리고 .onEventEmitter 메서드는 콜백을 예상하므로 다음에 수행하려는 모든 작업은 해당 콜백에서 발생해야 합니다. readline 모듈은 수신할 line 이벤트를 제공합니다.

    솔루션 #1



    처음에는 "들어오는 데이터를 외부 배열로 푸시"접근 방식을 시도했습니다.

    const incomingData = [];
    
    rl.on('line', data => [
      incomingData.push(data);
    ])
      .on('close', () => {
        // do something with incomingData
      });
    


    이 솔루션은 하나의 파일만 읽는 경우 실제로 작동합니다. 불행하게도 파일 디렉토리를 반복하고 각 파일을 읽은 다음 데이터로 작업을 수행해야 합니다. 나는 카운터와 그렇지 않은 모든 종류의 일에 지쳤지만 루프와 다음에 일어날 필요가 있는 경쟁 조건에 계속 부딪쳤습니다. 따라서 나를 위한 해결책은 아닙니다.

    솔루션 #2



    이 솔루션은 실제로 내 로컬 코드 멘토링meetup 회원에게서 나왔습니다. 이 솔루션은 약속을 사용합니다.

    먼저 다양한class 요구에 맞는 JavaScript.csv를 만들었습니다.

    const fs = require('fs');
    const readline = require('readline');
    const path = require('path');
    
    class CSVHelpers {
      constructor () {
        super();
      }
    
      /**
       * @param  {string} filePath
       * @return {promise} Array of row objects. Key: header, value: field value
       */
      read (filePath) {
        return new Promise ((resolve, reject) => {
          try {
            const reader = this._createReadStream(filePath);
            let rows = [];
            let headers = null;
    
            reader.on('line', row => {
              if (headers === null) {
                headers = row.split(',');
              } else {
                const rowArray = row.split(',');
                const rowObject = {};
                rowArray.forEach((item, index) => {
                  rowObject[headers[index]] = item;
                });
    
                rows.push(rowObject);
              }
            })
              .on('close', () => {
                resolve({
                  rows,
                  file: filePath
                });
              });
          } catch (error) {
            reject(error);
          }
        });
      }
    
      /**
       * @param  {type} filePath
       * @return {type} Readline event emitter
       */
      _createReadStream (filePath) {
        const fd = fs.openSync(path.resolve(filePath));
        const fileStream = fs.createReadStream(path.resolve(filePath), {fd});
        return readline.createInterface({
          input: fileStream
        });
      }
    }
    
    module.exports = CSVHelpers;
    


    그런 다음 내 코드에서

    const csv = new CSVHelpers();
    const dataFiles = fs.readdirSync(<pathToDirectory);
    
    const filePromises = dataFiles.map(file => {
      return csv.read(<pathToFile>);
    });
    
    Promise.all(filePromises)
      .then(values => {
        // do something with the values.
      });
    


    Promise 접근 방식은 다음 루프나 콜백을 시도할 필요가 없음을 의미합니다.

    결론



    이것이 최상의 솔루션인지는 모르겠지만 제 사용 사례에 적합하고 제가 갖고 있던 경합 조건을 해결합니다. 문제를 해결할 수 있는 더 나은 방법이 있으면 알려주십시오.

    좋은 웹페이지 즐겨찾기