【问题标题】:Async/Await overkill? Am I unnecessarily using async and await?异步/等待矫枉过正?我是否不必要地使用异步和等待?
【发布时间】:2019-08-23 10:00:07
【问题描述】:

我想知道我的小节点脚本是否毫无意义或过度使用 async/await。我的目标是尝试并行查找和解析尽可能多的文件。我的想法是它会更快。

我的节点脚本遍历目录和任何子目录并查找 .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);

【问题讨论】:

  • 我看到的主要问题是所有try/catches。当出现错误时,您究竟希望发生什么?是要停止当前文件解析,还是停止当前目录解析,还是停止整个脚本,还是什么?
  • @CertainPerformance 在尝试熟悉 async/await 时,我读过的每个资源似乎都在重申同一点,那就是在使用 async/await 时始终使用 try/catch。但也许在我是新人的时候,我读错了,而且在我不需要的时候做得过火了。
  • 你能解释一下当你遇到错误时你想要发生什么吗?
  • 使用 async/await 时有关 try/catch 的建议更接近于“从不”而不是“总是”。
  • 除了日志记录,当错误发生时您要寻找的操作逻辑是什么?继续当什么事都没发生过?停止一切?这就是重构所依赖的

标签: javascript node.js async-await


【解决方案1】:

await 通常在特定代码块需要等待一个 Promise 然后在完成之前用它做某事时很有用(可能涉及在之后等待 另一个 Promise )。如果唯一的 await 位于函数的最后一行,那么只返回 Promise 会更有意义。

对于try/catch,总体思路是在可以适当处理的级别上捕获错误。因此,例如,如果您想在出现问题时完全停止,只需在最外层调用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 可能失败,但您不希望错误发生渗透调用链并停止一切,try/catchwriteFile 的调用者中:

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 的修复)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-07-07
    • 2016-03-25
    • 2017-10-09
    • 1970-01-01
    • 2017-12-11
    • 1970-01-01
    相关资源
    最近更新 更多