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

dav*_*vid 1 javascript node.js promise async-await node.js-fs

我正在尝试扫描本地目录以查找其中的所有文本文件,然后读取每个文件。我想使用 Promise、async 和 wait 来正确地完成它,但是出了问题,并且我一直在试图解决它。

我能够读取目录内容并正确输出所有文件名,但是当我尝试使用该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}`);
  }
})();
Run Code Online (Sandbox Code Playgroud)

以下是该 javascript 的完整控制台输出,其中“Promise { }”日志被注释掉:

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
Run Code Online (Sandbox Code Playgroud)

我希望使工作解决方案尽可能接近此代码,因为它实际上是一个更大的难题的一部分,并且我需要能够继续利用此结构。

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


更新:检查下面的答案以获得问题的最准确的解决方案:/sf/answers/5264421571/

RAl*_*len 5

NodeJS 有 promisified 版本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}`);
  }
})();
Run Code Online (Sandbox Code Playgroud)

如果您想修复原始代码,则需要进行一些更改。需要进行第一个更改,因为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);
    });
  });
};
Run Code Online (Sandbox Code Playgroud)

第二个改变是你的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';
};
Run Code Online (Sandbox Code Playgroud)

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

请让我知道这可不可以帮你。