You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读写文件前检查文件/文件夹存在性的方案是否存在竞态条件?

你的代码仍存在竞态条件

是的,你当前的代码依然有竞态条件风险,问题出在检查操作(fs.access)和后续的创建/写入操作之间存在时间窗口:

举两个典型场景:

  1. 当checkIfFolderOrFileExist返回文件/文件夹不存在后,在你调用createFolder之前,另一个进程或线程创建了目标文件夹,此时你的createFolder会因为文件夹已存在而抛出错误。
  2. 当checkIfFolderOrFileExist返回文件存在后,在你调用writeToTheFile之前,另一个进程或线程删除了该文件,若文件所在文件夹也被删除,writeFile会直接报错;即使文件夹还在,若你的业务逻辑依赖“覆盖原有文件”的预期,也可能出现不符合预期的行为。

如何彻底规避竞态条件

正确的思路是直接执行目标操作,然后捕获并处理操作过程中出现的错误,因为提前检查和执行操作不是原子性的,中间的时间差总会有被外部干扰的可能。

针对你的场景,可以调整代码如下:

方案1:利用fs.mkdir的recursive参数(推荐)

将createFolder的recursive设为true,这样即使文件夹已存在也不会报错,跳过提前检查的步骤:

const createFolder = () => {
  return new Promise((resolve, reject) => {
    fs.mkdir(folderPath, { recursive: true }, (err) => {
      if (err) {
        reject(err);
      } else {
        resolve("done");
      }
    });
  });
};

const callBothPromiseFunction = async () => {
  try {
    // 先确保文件夹存在,不管之前有没有
    await createFolder();
    // 直接写入文件,不存在则创建,存在则覆盖
    await writeToTheFile(someRandomData);
    // 其他逻辑
  } catch (err) {
    // 统一处理错误
    console.error(err);
  }
};

方案2:直接写入,捕获路径不存在的错误后再创建文件夹重试

如果不需要提前确保文件夹存在,也可以直接尝试写入,捕获ENOENT(路径不存在)错误后再创建文件夹并重试:

const callBothPromiseFunction = async () => {
  try {
    await writeToTheFile(someRandomData);
  } catch (err) {
    if (err.code === 'ENOENT') {
      // 路径不存在,先创建文件夹
      await createFolder();
      // 重试写入
      await writeToTheFile(someRandomData);
    } else {
      // 处理其他错误
      throw err;
    }
  }
  // 其他逻辑
};

总结

所有“先检查后操作”的文件系统操作模式都存在竞态条件风险,因为检查和操作不是原子操作。正确的做法是直接执行操作,通过错误处理来应对不同的前置状态,这也是Node.js官方文档推荐的方式。

内容的提问来源于stack exchange,提问作者JustANewCoder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 20:50:26