为什么 node.js `fs.existsSync` 在包装在 promise 中时不能很好地工作?

Yos*_*Yos 2 javascript asynchronous fs node.js

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

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

然后我还想包装fs.existsSync在承诺中以完成我的功能,但包装它会导致偶尔的错误行为,即,如果文件目录不存在而我想创建一个目录,则该目录将被创建为空而没有文件. 通过调试,我发现只有同步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);
          }
        }());
      }
    }));
  }
Run Code Online (Sandbox Code Playgroud)

我错过了什么以及如何将我的功能变成真正的异步?

Est*_*ask 5

偶尔的不正确行为,即,如果文件目录不存在而我想创建一个目录,则该目录将在没有文件的情况下创建为空

这可能是由 和 的不正确实现引起的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();
      });
    }));
  }
Run Code Online (Sandbox Code Playgroud)

util.promisify是为了:

const writeFilePromise = util.promisify(fs.writeFile);
Run Code Online (Sandbox Code Playgroud)

即便如此,这也是一次轮子改造,因为已经有第三方软件包可以做到这一点,甚至更多,即fs-extra.

createFile控制流程很差,并使用了promise构造反模式。由于它使用async..await,它应该是:

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

需要说明的existsSync是,这是一个稳定的API方法,可以用它来检查文件是否存在。正如文档所述,

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

  • @PatrickRoberts 没错,检查文件是否存在的概率为非零概率为假阳性或假阴性,而existsSync 则较低。我不认为这是一个真正的问题,只是一个可以用作经验法则​​的好做法。我更新了答案,澄清了存在与存在同步。IIRC,几年前我几乎陷入了同样的陷阱。 (2认同)