使用Promises/Async/Await从Node.js目录中读取多个文件



我试图扫描本地目录中的所有文本文件,然后读取每个文件。我想用promise, async和await来正确地完成它,但是有些东西出错了,我一直在试图找出它。

我能够正确读取目录内容并输出所有文件名,但是当我尝试使用map方法读取这些文件本身时,我得到每个文件的Promise { <pending> }日志。此外,我得到一个UnhandledPromiseRejection错误在结束。

我可以在map函数之外读取单个文件,但不能循环遍历所有文件并读取它们而不会出现错误。

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const textFileDir = `${__dirname}/files/`;
const readDirPro = (dir) => {
return new Promise((resolve, reject) => {
fs.readdir(dir, (error, files) => {
if (error) reject('Could not find directory');
resolve(files);
});
});
};
const readFilePro = (file) => {
return new Promise((resolve, reject) => {
fs.readFile(file, 'utf-8', (err, data) => {
if (err) reject('Could not find file');
resolve(data);
});
});
};
const readAllFiles = async () => {
try {
const dirFilesArr = await readDirPro(textFileDir);
// correctly outputs array of file names in directory
// console.log('dirFilesArr: ', dirFilesArr);
// THIS IS THE PROBLEM:
await dirFilesArr.map((file) => {
// console.log(file);
// correctly outputs filenames
const fileContent = readFilePro(file);
// console.log(fileContent);
// incorrectly outputs "Promise { <pending> }" logs for each file
});
} catch (err) {
console.log(`catch (err): ${err}`);
throw err;
}
return '2: final return';
};
(async () => {
try {
console.log('1: readAllFiles!');
const x = await readAllFiles();
console.log(x);
console.log('3: Done!');
} catch (err) {
console.log(`IIFE catch ERROR 💥: ${err}`);
}
})();

这是该javascript的完整控制台输出,注释掉了' Promise {} logs:

1: readAllFiles!
2: final return
3: Done!
node:internal/process/promises:289
triggerUncaughtException(err, true /* fromPromise */);
^
[UnhandledPromiseRejection: This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). The promise rejected with the reason "Could not find file".] {
code: 'ERR_UNHANDLED_REJECTION'
}
Node.js v19.0.1

我更喜欢保持工作解决方案尽可能接近这段代码,因为它实际上是一个更大的谜题的一部分,我需要能够继续利用这个结构。

我的最终目标是读取所有这些文件,从中提取某些数据,然后将这些数据写入新的JSON文件。


更新:下面是这个问题最准确的答案:https://stackoverflow.com/a/75206022/3787666

NodeJS有fs的承诺版本,所以从技术上讲,你可以通过以下代码实现预期的结果:

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const textFileDir = `${__dirname}/files/`;
(async () => {
try {
console.log('1: readAllFiles!');
const dirFilesArr = await fs.promises.readdir(textFileDir);
const files = await Promise.all(dirFilesArr.map((file) => {
const fullPath = path.join(textFileDir, file);
return fs.promises.readFile(fullPath, { encoding: 'utf-8' });
}));
console.log(files);
console.log('3: Done!');
} catch (err) {
console.log(`IIFE catch ERROR 💥: ${err}`);
}
})();

如果你想修复你的原始代码,你需要做一些改变。首先需要进行更改,因为readdir不返回具有完整路径的文件名,因此需要为每个文件添加路径以读取文件。因此,您的readFilePro应该看起来像这样:

const readFilePro = (file) => {
return new Promise((resolve, reject) => {
fs.readFile(path.join(textFileDir, file), 'utf-8', (err, data) => {
if (err) reject('Could not find file');
resolve(data);
});
});
};

第二个更改是您的readAllFiles需要更改如下:

const readAllFiles = async () => {
try {
const dirFilesArr = await readDirPro(textFileDir);
return Promise.all(dirFilesArr.map((file) => readFilePro(file)));
} catch (err) {
console.log(`catch (err): ${err}`);
throw err;
}
return '2: final return';
};

在您的原始代码中,您为每个文件运行readFilePro并检索您从不等待的承诺。上面的方法解决了这个问题。

如果有帮助,请告诉我。

在上述公认答案的基础上,这里有一个补充答案,它解决了我问题的一个关键方面。而不是返回所有的数据在一个大块从map,我真的需要能够操作数据从每个文件,只要它被读取(为什么我有const fileContent在我上面的问题)。所以,这就是我现在如何处理,通过使用async/await的回调map方法。如果我想将const files数据传递给Promise.all之后的函数,我仍然需要Promise.all方法和return

const files = await Promise.all(
dirFilesArr.map(async (file, i) => {
const fullPath = path.join(textFileDir, file);
const fileContent = await fs.promises.readFile(fullPath, {
encoding: 'utf-8',
});
//console.log(fileContent); // can now view file data individually
await externalFunction(fileContent, i); // manipulating file data
// return fs.promises.readFile(fullPath, { encoding: 'utf-8' });
})
);

最新更新