Чтение нескольких файлов асинхронно в node.js - PullRequest
1 голос
/ 17 октября 2019

Мое приложение требует чтения папки, содержащей несколько файлов. Все файлы должны быть получены асинхронно. Вывод всех файлов должен быть объединен в один массив. Чтобы добиться этого, приведенный ниже код был сделан. (Я использовал обещание). Для одного файла это работает, но для нескольких файлов это не работает. Нужны ваши предложения.

код из files.js

function readFolder(FolderPath){
    return new Promise(function(resolve, reject){
        fs.readdir(FolderPath, (err, files) => {
            if (err) {
                logger.error(`Folder (${FolderPath}) reading Failed :` + err)
                reject(error = "Reading Folder failed")
            } else {
                console.log('resolved')
                resolve(files)
            }
        })
    })
};

function readFile(FilePath){
    return new Promise(function(resolve,reject){
        fs.readFile(FilePath, (err, data) => {
            if (err) {
                reject(error = "Reading file failed") 
            } else {
                console.log('Read File started :'+FilePath)
                var chunk = data.toString();
                var lines = chunk.split('\n');
                var routes = []
                for(var line = 0; line < lines.length; line++){
                    if (lines[line].match(/router.post/)){
                        currentRoute = lines[line].substring(lines[line].indexOf("'")+1 , lines[line].lastIndexOf("'"))
                        route = "/api/angular" + currentRoute
                        routes.push(route)
                    }
                }
                if (routes !== []){
                    console.log('routes for file is :' + routes)
                }

                resolve(routes)
            }
         })
    })
};

function readFiles(FilePaths){
    return new Promise(function(resolve, reject){
        let routesArray = []
        FilePaths.forEach(FilePath => {
            console.log("File Path :"+FilePath)
            readFile(FilePath)
            .then((routes) => {
                console.log('Concatinate')
                routesArray = routesArray.concat(routes)
            })
            .catch((error) => {
                console.log(error)
            })
        })
        console.log(routesArray)
        resolve(routesArray)
    })
}

имя файла: api.js (призыв к обещанию)


const files = require('./../controlers/files')
files.readFolder(FolderPath)
    .then((filesArray) => {
        var FilePaths = [];
        filesArray.forEach(file => {
            path = "routes/"+file
            FilePaths.push(path)
        })
        console.log(FilePaths)
        return files.readFiles(FilePaths)
    })
    .then((routes) => {
        console.log('routes :', routes)
        res.status(200).send(routes)
    })
    .catch((error) => {
        response.message = "Folder or file Reading failed";
        response.success = false;
        res.status(500).send(response);
    })

Пожалуйста, подскажите, где я не прав.

Ответы [ 2 ]

1 голос
/ 17 октября 2019

Вы "не правы", когда используете .forEach с синтаксисом Promise (в функции readFiles). .forEach - это функция стиля «обратного вызова», она не будет работать так, как вы ожидаете с Promise.

Вам нужно подождать, пока все файлы не будут выполнены, мое предложение использует Array.map и Promise.all:

function readFiles(FilePaths) {
  return new Promise(function (resolve, reject) {
    let routesArray = []
    const promises = FilePaths.map(FilePath => { // get back an array of promises
      console.log("File Path :" + FilePath)
      return readFile(FilePath)
        .then((routes) => {
          console.log('Concatinate')
          routesArray.push(...routes) // I like .push function
        })
        .catch((error) => {
          console.log(error)
        })
    });
    Promise.all(promises)
      .then(() => { // all done!
        console.log(routesArray)
        resolve(routesArray)
      })
  })
}
0 голосов
/ 17 октября 2019

Поскольку мы говорим о нескольких файлах, могу я предложить создать функцию readAsync и передать ей некоторые параметры ('utf8' здесь) и callback?

function readAsync(file, callback) {
  fs.readFile(file, 'utf8', callback);
}

После этогомы могли бы map закончить асинхронно и попытаться прочитать содержимое, например так:

// we're passing our files, newly created function, and a callback

async.map(files, readAsync, (err, res) => { 
        // res = ['file 1 content', 'file 2 content', ...]
    });

Я полагаю, что вы можете сократить весь процесс до этого:

// Let's put everything inside of one function:

function testAsync() {
    const files = ['file1.json', 'file2.json', 'file3.json'];
    async.map(files, fs.readFile, function (err, data) {
        for(let file of files) {
            console.log( file.toString() );
        }
    });
}

Вот пример кода с использованием обещаний:

function readFromFile(file) {
    return new Promise((resolve, reject) => {
        fs.readFile(file, (err, data) => {
            if (err) {
                console.log(err);
                reject(err);
            }
            else {
                resolve(JSON.parse(data));
            }
        });
    });
}

// First, an array of promises are built.
// Each Promise reads the file, then calls resolve with the result.
// This array is passed to Promise.all(), which then calls the callback, 
// passing the array of results in the same order.

const promises = [
    readFromFile('result1.json'),
    readFromFile('result2.json')
    // ETC ...
];

Promise.all(promises)
 .then(result => {
    baseList = result[0];
    currentList = result[1];
    // do more stuff
});
...