2

Работаю над небольшим проектом, где стоит задача считать json файлы в папке , на их основе собрать строку и вернуть ее. Казалось бы - просто, но я завис на моменте асинхронности, уже как только не пробовал и менял на Sync методы Ноды, и async/await, но у меня не получается вернуть контент. Функция возвращает значение до того, как все файлы прочитаются и сформируется результирующая строка.

Это текущий вид файлов:

 function loadReport(user) {
    const data =  fs.readFileSync(path.join(__dirname, '../reports', user))
    return JSON.parse(data).content
}
//Generate full report using reports files
const  fullyReportGenerator = async function(){
     var fullReport = ''
     var finalRep = await fs.readdir(path.join(__dirname, '../reports'), (err , files)=>{
        files.forEach((user) =>  {
                fullReport += loadReport(user)
        })
       return fullReport // Вот тут в переменной все то, что надо
})

return finalRep; // А вот тут уже undefined

}

Вызываю потом эту функцию в другом файле:

bot.command('/report', (ctx) => {
    fullyReportGenerator()
})

Я понимаю, что ответ на поверхности, но я уже немного запутался и прошу совета.

dmitriy_vlz
  • 1,533
  • у тебя тут такой зоопарк... прочитай https://ru.stackoverflow.com/questions/554290 (и https://ru.stackoverflow.com/a/1178589/2659). пингани меня через собачку, если не поможет, я поясню на твоём коде – nörbörnën Sep 17 '20 at 12:14
  • fs.readdir() не возвращает промис, поэтому await тут бесполезен. Как вариант, используйте fs-extra вместо fs. – Yaant Sep 17 '20 at 12:27
  • @Yaant не возвращает, это - верно, но fs-extra тут лишняя так как есть https://nodejs.org/dist/latest/docs/api/fs.html#fs_fspromises_readdir_path_options – nörbörnën Sep 17 '20 at 12:32
  • @Yaant , да, тут я уже просто сам себя запутал и не могу распутаться. Я вообще вычитал, что если подключить как const fs = require('fs').promises;, то все функции будут возвращать промис. – dmitriy_vlz Sep 17 '20 at 12:34
  • @nörbörnën, а, да, тут я отстал от жизни. Забыл, что в fs добавили нативную поддержку промисов. Просто у нас тут проект базируется на 8 ноде, а там такого не было. :) – Yaant Sep 17 '20 at 12:50
  • @nörbörnën похоже, мне все таки нужна помощь. Я все с нуля переписал, но мне кажется "зоопарк" только больше стал. Меня пугает то, что у меня внутри одного промиса - другой и я теряюсь, как это правильно организовать. – dmitriy_vlz Sep 17 '20 at 12:55
  • @Yaant эх, я тебя понимаю – nörbörnën Sep 17 '20 at 13:00
  • так какую функцию ты в итоге вызываешь? с promise или без? – Grundy Sep 17 '20 at 13:06

1 Answers1

6

Асинхронно на async/await

const { promises: fs } = require('fs');
const path = require('path');

const bot = { command: (cmd, cb) => cb({ctx: 'ctx'}) };

bot.command('/report', (ctx) => { fullyReportGenerator() .then((reportRawData) => { console.log(reportRawData); console.log(ctx); }) .catch(console.error); });

async function fullyReportGenerator() { const reportDirectoryPath = path.join(__dirname, '../reports'); const reportFiles = await readDir(reportDirectoryPath);

const reportData = []; for (const filePath of reportFiles) { const reportRawDataItem = await readFile(filePath);

if (typeof reportRawDataItem === 'object' && 'content' in reportRawDataItem) {
  reportData.push(reportRawDataItem.content);
}

}

return reportData; }

async function readDir(dirPath) { const files = await fs.readdir(dirPath); return files.map((x) => path.join(dirPath, x)); }

async function readFile(filePath) { const data = await fs.readFile(filePath, {encoding: 'utf-8'}); try { const json = JSON.parse(data); return json; } catch (err) { console.error(err); } return data; }

Приведённый код является не более чем иллюстрацией подхода к решению задачи или решением для небольшого количества небольших отчётов.

Не учитывает, что в каталоге report могут быть подкаталоги (решается проверками и рекурсивным вызовом readDir).

Чтение файлов происходит последовательно, но при большом количестве файлов нужно распараллелить (я использую p-queue).

Не оптимально с тз производительности, если файлы отчётов большого размера, но можно уйти на стримы.

Полностью синхронный

На небольших количествах файлов и небольших данных будет, скорее всего, работать быстрее.

const fs = require('fs');
const path = require('path');

const bot = { command: (cmd, cb) => cb({ctx: 'ctx'}) };

bot.command('/report', (ctx) => { const reportRawData = fullyReportGenerator(); console.log(reportRawData); console.log(ctx); });

function fullyReportGenerator() { const reportDirectoryPath = path.join(__dirname, '../reports'); const reportFiles = readDir(reportDirectoryPath);

const reportData = []; for (const filePath of reportFiles) { const reportRawDataItem = readFile(filePath);

if (typeof reportRawDataItem === 'object' && 'content' in reportRawDataItem) {
  reportData.push(reportRawDataItem.content);
}

}

return reportData; }

function readDir(dirPath) { const files = fs.readdirSync(dirPath); return files.map((x) => path.join(dirPath, x)); }

function readFile(filePath) { const data = fs.readFileSync(filePath, {encoding: 'utf-8'}); try { const json = JSON.parse(data); return json; } catch (err) { console.error(err); } return data; }

nörbörnën
  • 12,192
  • 5
  • 29
  • 40