异步/等待过度使用?我是否不必要地使用了异步和等待?

3

我想知道我的小型node脚本是否过于使用async/await,或者使用得太多了。我的目标是尝试尽可能并行地查找和解析尽可能多的文件,我认为这样会更快。

我的node脚本遍历一个目录及其子目录,并寻找.docx文件。当它找到它们时,它通过Mammoth将它们转换为.html文件。它只是将它们放在类似的目录结构中。

我的代码可以工作,但我是否过度使用了async/await?有没有可以消除它们使用的地方,因为我使用它们的理由不存在?

const createTempDirs = async (catMap) => {
  try {
    const dirPromises = catMap.map((cat) => fs.mkdir(`issue/${cat.abv}`, {
      recursive: true,
    }));
    await Promise.all(dirPromises);  
  } catch (error) {
    console.log(error);
  }
};

const writeToFile = (fileName) => {
  return async (result) => {
    //return await fs.writeFile(`issue/${fileName.replace('.docx', '.html')}`);
    try {
      const [
        ,
        category,
        ...parts
      ] = fileName.split(' ');
      await createTempDirs(catMap),
      await fs.writeFile(`issue/${getShortCatName(category)}/${fileName.replace('.docx', '.html')}`, result.value);
    } catch (error) {
      console.log(error);
    }
  };
}

const fileToHTML = async (file, dirPath) => {
  try {
    const fileDetails = await fs.lstat(dirPath + file);
    if (fileDetails.isDirectory()) {
      walkDir(dirPath + addTrailingSlash(file));
    }
    if (!fileDetails.isDirectory() && path.extname(file) === '.docx') {
      mammoth.convertToHtml({
        path: dirPath + file,
      }, conversionOptions).then(writeToFile(file));
    }
  } catch (error) {
    console.log(error);
  }
};

const processFiles = async (files, dirPath) => {
  try {
    const filePromises = files.map(file => fileToHTML(file, dirPath));
    return await Promise.all(filePromises);
  } catch (error) {
    console.log(error);
  }
};

const walkDir = async (dirPath) => {
  try {
    const files = await fs.readdir(dirPath);
    processFiles(files, dirPath);
  } catch (error) {
    console.log(error);
  }
};

walkDir(dirPath);


2
我看到的主要问题是所有的 try/catch。当出现错误时,你究竟想要发生什么?你想要停止当前文件解析、停止当前目录解析、停止整个脚本,还是其他什么? - CertainPerformance
@CertainPerformance 在尝试熟悉async/await时,我读过的每个资源似乎都在重申同一点,那就是在使用async/await时始终要使用try/catch。但也许由于我是新手,我可能会在不需要的情况下读错并过度使用它。 - user11965064
1
你能解释一下当你遇到错误时具体想要发生什么吗? - CertainPerformance
2
除了日志记录之外,当出现错误时,您正在寻找的“操作逻辑”是什么?继续进行好像什么都没有发生?停止一切?这就是重构所依赖的。 - CertainPerformance
@CertainPerformance 目前它们实际上没有任何操作逻辑。不过,如果能够对错误进行处理就好了。也许可以停止操作并提示输入有效目录,或者告知用户未找到 .docx 文件。 - user11965064
显示剩余3条评论
1个回答

2
await通常在特定的代码块需要等待一个Promise执行完成后才能继续执行(可能需要在这之后再等待另一个Promise)时发挥作用。如果只有一个await位于函数的最后一行,那么直接返回Promise会更加合理。
至于try/catch,一般的思路是将错误捕获在适当处理它们的级别上。因此,例如,如果想要在出现问题时完全停止,只在最外层调用中捕获,如下所示:
const createTempDirs = (catMap) => Promise.all(
  catMap.map((cat) => fs.mkdir(`issue/${cat.abv}`, {
    recursive: true,
  }))
);

const writeToFile = (fileName) => {
  return async (result) => {
    //return await fs.writeFile(`issue/${fileName.replace('.docx', '.html')}`);
    const [
      ,
      category,
      ...parts
    ] = fileName.split(' ');
    await createTempDirs(catMap);
    await fs.writeFile(`issue/${getShortCatName(category)}/${fileName.replace('.docx', '.html')}`, result.value);
  };
};

const fileToHTML = async (file, dirPath) => {
  const fileDetails = await fs.lstat(dirPath + file);
  if (fileDetails.isDirectory()) {
    // see below line - remember to await or return every Promise created!
    await walkDir(dirPath + addTrailingSlash(file));
  }
  if (!fileDetails.isDirectory() && path.extname(file) === '.docx') {
    // see below line - remember to await or return every Promise created!
    return mammoth.convertToHtml({
      path: dirPath + file,
    }, conversionOptions).then(writeToFile(file));
  }
};

const processFiles = (files, dirPath) => Promise.all(files.map(file => fileToHTML(file, dirPath)));

const walkDir = async (dirPath) => {
  const files = await fs.readdir(dirPath);
  processFiles(files, dirPath);
};

然后,在入口点只有catch,调用walkDir

最初的回答:

walkDir(dirPath)
  .catch((err) => {
    // There was an error somewhere
  });

如果你想在子调用出现错误时仍然保持某个点的处理,那么就在该点捕获错误,例如如果单个writeFile可能失败,但你不希望错误向上传递到调用链并停止所有操作,则在writeFile的调用者中使用try/catch

最初的回答:

如果您想在子调用中出现错误时仍然保持某个点的处理,请在该点捕获错误。例如,如果单个writeFile可能失败,但您不希望错误向上游传播并停止所有内容,请在writeFile的调用者中使用try/catch

const writeToFile = (fileName) => {
  return async (result) => {
    const [
      ,
      category,
      ...parts
    ] = fileName.split(' ');
    await createTempDirs(catMap);
    try {
      await fs.writeFile(`issue/${getShortCatName(category)}/${fileName.replace('.docx', '.html')}`, result.value);
    } catch(e) {
      // writeFile failed, but the rest of the script will continue on as normal
      // the error, since it was caught here,
      // WILL NOT BE passed up to the caller of writeToFile
    }
  };
};

确保在入口点之后,每个创建的Promise都要使用awaitreturn关键字处理,以便错误能够正确地上报到Promise链中。(请参考对fileToHTML所做的修复)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接