Почему node.js `fs.existsSync` не работает хорошо, когда обернут в обещание? - PullRequest
0 голосов
/ 08 июня 2018

Я пишу функцию createFile для создания файла в каталоге, если он еще не существует.Я использую собственный пакет Node.js fs для выполнения всех файловых операций.Я хотел сделать свою функцию асинхронной, поэтому я обернул все fs функции в обещания:

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();
    }));
  }

Затем я хотел также обернуть 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);
          }
        }());
      }
    }));
  }

Чего мне не хватает и как я могу превратить свою функцию в действительно асинхронную?

Ответы [ 3 ]

0 голосов
/ 08 июня 2018

Прежде всего, вы структурировали writeFilePromise и mkDirPromise так, чтобы они всегда были resolve и никогда reject.Так как 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;
        }
    }
}

Помнитеиспользовать await createFile(dir, fileName, httpMethod, textContent) при вызове этой функции.

0 голосов
/ 08 июня 2018

Во-первых, рассмотрите возможность отклонения с чем-то значимым, а не просто reject()

Поскольку вы думаете с асинхронностью и обещанием, я не рекомендую использовать fs.xxxSync() функции.Кроме того, fs.exists устарела, попробуйте использовать fs.stat().

Я предполагаю, что вы создадите файл, только если HTTP-метод POST, но файл всегда будет создаваться, если HTTP-метод не POST втекущая логика if-else.

Нет необходимости создавать немедленно вызываемую асинхронную функцию.

Попробуйте это:

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);
                }
            });
        });
    });
}
0 голосов
/ 08 июня 2018

случайное некорректное поведение, а именно, если каталог для файла не существует, и я хочу создать его, каталог будет создан пустым без файла

Это может быть вызванонеправильными реализациями 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();
      });
    }));
  }

Вот для чего util.promisify:

const writeFilePromise = util.promisify(fs.writeFile);

Даже в этом случае это переосмысление колеса, потому что уже есть сторонние пакеты, которые делают этои даже больше, а именно fs-extra.

createFile имеет слабый поток управления и использует многообещающую конструкцию antipattern.Поскольку он использует 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);
  }
}

Следует отметить, что existsSync - это стабильный метод API, его можно использовать для проверки существования файла.Поскольку документация гласит,

Обратите внимание, что fs.exists () устарела, а fs.existsSync () - нет.(Параметр обратного вызова для fs.exists () принимает параметры, которые несовместимы с другими обратными вызовами Node.js. Fs.existsSync () не использует обратный вызов.)

...