Javascript 为什么node.js`fs.existsSync`不';在承诺的约束下,你不能很好地工作吗?

Javascript 为什么node.js`fs.existsSync`不';在承诺的约束下,你不能很好地工作吗?,javascript,node.js,asynchronous,fs,Javascript,Node.js,Asynchronous,Fs,我正在编写一个函数createFile,在目录中创建一个文件,除非它已经存在。我使用Node.js nativefs包来执行所有文件操作。我想使我的函数异步,所以我将所有fs函数包装在promises中: function writeFilePromise(writePath, textContent) { return new Promise((resolve, reject) => { fs.writeFile(writePath, textContent, (er

我正在编写一个函数
createFile
,在目录中创建一个文件,除非它已经存在。我使用Node.js native
fs
包来执行所有文件操作。我想使我的函数异步,所以我将所有
fs
函数包装在promises中:

function writeFilePromise(writePath, textContent) {
    return new Promise((resolve, reject) => {
      fs.writeFile(writePath, textContent, (err) => {
        reject();
      });
      resolve();
    });
  }

  function mkDirPromise(dir) {
    return new Promise(((resolve, reject) => {
      fs.mkdir(path.join(constants.FILES_STORAGE_DIR, dir), (err) => {
        reject(err);
      });
      resolve();
    }));
  }
然后,为了完成我的功能,我还想在promise中包装
fs.existsSync
,但包装它会导致偶尔的错误行为,即,如果文件的目录不存在,并且我想创建一个目录,那么在没有文件的情况下,目录将被创建为空。通过调试,我发现只有synchronous
fs.existsSync
始终能够工作。 这是功能代码:

function createFile(dir, fileName, httpMethod, textContent) {
    return new Promise(((resolve, reject) => {
      const searchPath = path.join(ROOT_DIR, dir, fileName);
      if (httpMethod === POST && fs.existsSync(searchPath)) {
        reject();
      } else {
        const fileExistsStatus = fs.existsSync(path.join(ROOT_DIR, dir));
        (async function fsOperations() {
          try {
            if (!fileExistsStatus) {
              await mkDirPromise(dir);
            }
            await writeFilePromise(searchPath, textContent);
            resolve();
          } catch (err) {
            reject(err);
          }
        }());
      }
    }));
  }
我遗漏了什么?我如何将我的函数转换成真正的异步函数

偶尔出现错误行为,即,如果文件的目录不存在,并且我想创建一个目录,那么在没有文件的情况下,目录将被创建为空

这可能是由于
writeFilePromise
的不正确实现,尤其是
mkDirPromise
造成的
fs.writeFile
fs.mkdir
是异步的,但承诺是同步解析的。应该是:

  function writeFilePromise(writePath, textContent) {
    return new Promise((resolve, reject) => {
      fs.writeFile(writePath, textContent, (err) => {
        if (err)
          reject(err);
        else
          resolve();
      });
    });
  }

  function mkDirPromise(dir) {
    return new Promise(((resolve, reject) => {
      fs.mkdir(path.join(constants.FILES_STORAGE_DIR, dir), (err) => {
        if (err)
          reject(err);
        else
          resolve();
      });
    }));
  }
async function createFile(dir, fileName, httpMethod, textContent) {
  const searchPath = path.join(ROOT_DIR, dir, fileName);
  if (httpMethod === POST && fs.existsSync(searchPath)) {
    throw new Error();
  } else {
    const fileExistsStatus = fs.existsSync(path.join(ROOT_DIR, dir));
    if (!fileExistsStatus) {
      await mkDirPromise(dir);
    }
    await writeFilePromise(searchPath, textContent);
  }
}
这就是
util.promisify
的作用:

const writeFilePromise = util.promisify(fs.writeFile);
即使这样,这也是一个轮子的重新发明,因为已经有第三方软件包可以做到这一点,甚至更多,即
fs extra

createFile
的控制流很差,并使用承诺构造反模式。由于它使用的是
async..await
,因此应该是:

  function writeFilePromise(writePath, textContent) {
    return new Promise((resolve, reject) => {
      fs.writeFile(writePath, textContent, (err) => {
        if (err)
          reject(err);
        else
          resolve();
      });
    });
  }

  function mkDirPromise(dir) {
    return new Promise(((resolve, reject) => {
      fs.mkdir(path.join(constants.FILES_STORAGE_DIR, dir), (err) => {
        if (err)
          reject(err);
        else
          resolve();
      });
    }));
  }
async function createFile(dir, fileName, httpMethod, textContent) {
  const searchPath = path.join(ROOT_DIR, dir, fileName);
  if (httpMethod === POST && fs.existsSync(searchPath)) {
    throw new Error();
  } else {
    const fileExistsStatus = fs.existsSync(path.join(ROOT_DIR, dir));
    if (!fileExistsStatus) {
      await mkDirPromise(dir);
    }
    await writeFilePromise(searchPath, textContent);
  }
}
应该提到的是,
existsSync
是一种稳定的API方法,可以使用它来检查文件是否存在。作为国家

请注意,不推荐使用fs.exists(),但不推荐使用fs.existsSync()。(fs.exists()的回调参数接受与其他Node.js回调不一致的参数。fs.existsSync()不使用回调。)


首先,您已经构建了
writeFilePromise
mkDirPromise
,这样它们将始终
解析
,而不会
拒绝
。因为
fs.writeFile
fs.mkdir
是异步的,一旦它们启动,线程立即移动到
resolve()
。我想你的意思是

function writeFilePromise(writePath, textContent) {
    return new Promise((resolve, reject) => {
        fs.writeFile(writePath, textContent, (err) => {
            if (err) reject();
            else resolve();
        });
    });
}

function mkDirPromise(dir) {
    return new Promise((resolve, reject) => {
        fs.mkdir(path.join(constants.FILES_STORAGE_DIR, dir), (err) => {
            if (err) reject();
            else resolve();
        });
    });
}
就fs.exists而言,这已经被去除了,所以我不建议使用它。相反,请尝试
fs.access

function accessPromise(dir) {
    return new Promise((resolve, reject) => {
        fs.access(dir, (err) => {
            if (err) reject();
            else resolve();
        });
    });
}
最后,尝试调整使用
async
函数声明的位置,以确保正确同步代码:

async function createFile(dir, fileName, httpMethod, textContent) {
    const searchPath = path.join(ROOT_DIR, dir, fileName);
    if (httpMethod === POST && await accessPromise(searchPath)) {
        return false;
    } else {
        const fileExistsStatus = await accessPromise(path.join(ROOT_DIR, dir));
        try {
            if (!fileExistsStatus) {
                await mkDirPromise(dir);
            }
            await writeFilePromise(searchPath, textContent);
            return true;
        } catch (err) {
            return false;
        }
    }
}

在调用该函数时请记住使用<代码>等待创建文件(dir、文件名、HTTPFrad、TrimeCype)< /C> >。首先,考虑用一些有意义的东西来拒绝,而不是只是“代码>拒绝”(< /代码>

)。 因为您使用的是async和promise,所以我不建议使用
fs.xxxSync()
函数。另外,
fs.exists
已被弃用,请尝试使用
fs.stat()

我猜您只会在HTTP方法为POST的情况下创建该文件,但在当前的if-else逻辑中,当HTTP方法不是POST时,将始终创建该文件

不需要创建立即调用的异步函数

试试这个:

function createFile(dir, fileName, httpMethod, textContent) {
    return new Promise((resolve, reject) => {
        const searchPath = path.join(ROOT_DIR, dir, fileName);
        if (httpMethod !== POST) {
            return reject(new Error('Invalid HTTP method'));
        }
        fs.exists(searchPath, (exists) => {
            if (exists) {
                return reject(new Error('Already exists'));
            }
            fs.exists(path.join(ROOT_DIR, dir), async (exists) => {
                try {
                    if (!exists) {
                        await mkDirPromise(dir);
                    }
                    await writeFilePromise(searchPath, textContent);
                    resolve();
                } catch (err) {
                    reject(err);
                }
            });
        });
    });
}

你的问题令人困惑。我想你可以说得更清楚。我认为您希望使用
fs.exists
而不是
fs.existsSync
,但是您在整个帖子中使用了
existsSync
,没有提及异步版本。您能更新您的帖子以包含不起作用的代码示例吗?@Gary fs.exists已被弃用。您真的不应该使用任何一个。只是尝试读取/写入文件并处理错误。
存在的问题是,在您尝试使用它时,您返回的值可能是错误的(除您的应用程序外,其他内容可能会编辑文件)。(编辑以删除关于existsSync被弃用的不正确声明)@Gary检查文件是否存在对我来说似乎没问题,因为读/写失败的原因可能有很多。是的,这是关于existsSync的一个很好的观点,existsSync失败的机会较小。无论如何,这里的问题是不正确的承诺。我投了更高的票,因为这确实有一些好的建议,但从根本上说,我觉得它没有解决问题。@PatrickRoberts,谢谢。我认为没有问题。有一种常见的误解,即
exists
existsSync
都不推荐使用,而只有
exists
不推荐使用,因为它的回调不一致,因为它不是第一个错误
existsSync
正在阻塞,但检查文件是否存在是少数几种有益的情况之一。我知道异步表单是唯一不推荐使用的表单,但同步表单被阻塞并不意味着另一个进程无法在这几百微秒之间修改文件的权限;)@PatrickRoberts没错,检查文件是否存在的概率不是零,而是假阳性或假阴性,使用existsSync的概率更低。我不认为这是一个真正的问题,只是一个可以作为经验法则的良好实践。我更新了答案,澄清了existsSync与existsSync的区别。IIRC,几年前我差点掉进同样的陷阱。