Использование файловой системы в node.js с помощью async / await

130

Я хотел бы использовать async / await с некоторыми операциями файловой системы. Обычно async / await отлично работает, потому что я использую babel-plugin-syntax-async-functions.

Но с этим кодом я сталкиваюсь с случаем if, где namesundefined:

import fs from 'fs';

async function myF() {
  let names;
  try {
    names = await fs.readdir('path/to/dir');
  } catch (e) {
    console.log('e', e);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

Когда я перестраиваю код в адскую версию обратного вызова, все в порядке, и я получаю имена файлов. Спасибо за подсказки.

Quellenangeber
источник

Ответы:

140

Начиная с узла 8.0.0, вы можете использовать это:

const fs = require('fs');
const util = require('util');

const readdir = util.promisify(fs.readdir);

async function myF() {
  let names;
  try {
    names = await readdir('path/to/dir');
  } catch (err) {
    console.log(err);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

См. Https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original

Azbykov
источник
7
В узле v8.9.4 появилось SyntaxError: Unexpected token importсообщение об ошибке. поддерживает ли node8 importтокен по умолчанию?
makerj
9
@makerj он использует новый importсинтаксис. В настоящее время требуется некоторая транспиляция. Можно было бы также использовать const fs = require('fs')илиconst { promisify } = require('util')
Джош Сэндлин
2
Вопрос нуба, а как {err, names} = functionназывается синтаксис?
Qasim
6
@Qasim это называется деструктурирующим присваиванием.
jaredkwright 05
1
@AlexanderZeitler Это может быть правдой. Я не смотрел, действительно ли это правильное использование деструктуризации. В случае async await, я думаю, вы бы просто сделали, names = await readdir('path/to/dir');и если errв catchблоке есть дескриптор . В любом случае название синтаксиса - деструктурирующее присвоение, которое было как раз в ответ на вопрос Касима.
jaredkwright
89

Встроенная поддержка функций async await fs начиная с Node 11

Начиная с Node.JS 11.0.0 (стабильный) и версии 10.0.0 (экспериментальный), у вас есть доступ к уже обещанным методам файловой системы, и вы можете использовать их с try catchобработкой исключений, а не проверять, содержит ли возвращаемое значение обратного вызова ошибка.

API очень чистый и элегантный! Просто используйте .promisesчлен fsобъекта:

import fs from 'fs';
const fsPromises = fs.promises;

async function listDir() {
  try {
    return fsPromises.readdir('path/to/dir');
  } catch (err) {
    console.error('Error occured while reading directory!', err);
  }
}

listDir();
Ътап
источник
Этот API стабилен начиная с версии 11.x, согласно документации по файловой системе на сайте Node.js
TheHanna
1
@DanStarns, если вы не return awaitобещаете, блок catch бесполезен ... Я думаю, что иногда полезно подождать, прежде чем вернуться
538ROMEO
@ 538ROMEO только что изучил это и ваше право. Спасибо, что указали на это.
DanStarns
Документация для этих альтернативных методов: nodejs.org/api/fs.html#fs_fs_promises_api
Дживан Тахар
87

Node.js 8.0.0

Собственный async / await

Promisify

В этой версии вы можете использовать встроенную функцию Node.js из библиотеки util .

const fs = require('fs')
const { promisify } = require('util')

const readFileAsync = promisify(fs.readFile)
const writeFileAsync = promisify(fs.writeFile)

const run = async () => {
  const res = await readFileAsync('./data.json')
  console.log(res)
}

run()

Обещание упаковки

const fs = require('fs')

const readFile = (path, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.readFile(path, opts, (err, data) => {
      if (err) reject(err)
      else resolve(data)
    })
  })

const writeFile = (path, data, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.writeFile(path, data, opts, (err) => {
      if (err) reject(err)
      else resolve()
    })
  })

module.exports = {
  readFile,
  writeFile
}

...


// in some file, with imported functions above
// in async block
const run = async () => {
  const res = await readFile('./data.json')
  console.log(res)
}

run()

Совет

Всегда используйте try..catchдля блоков ожидания, если вы не хотите повторно генерировать исключение наверху.

dimpiax
источник
Это странно. Я получаю SyntaxError: ожидание действительно только в асинхронной функции ... плачу от ярости.
Ведран Маричевич.
2
@VedranMaricevic. посмотри комментарии, awaitвсегда должен быть в asyncблоке :)
dimpiax
@VedranMaricevic. Вам нужно вызвать это const res = await readFile('data.json') console.log(res)в некоторой асинхронной функции
Jayraj
fs.promisesasync/await
Обертка
@PrimitiveNom Promise можно использовать традиционным способом внутри thenи catchт. Д. Где async / await - это современный поток поведения.
dimpiax
43

Вы можете вызвать неправильное поведение, потому что File-Api fs.readdirне возвращает обещание. Требуется только обратный вызов. Если вы хотите использовать синтаксис async-await, вы можете «обещать» функцию следующим образом:

function readdirAsync(path) {
  return new Promise(function (resolve, reject) {
    fs.readdir(path, function (error, result) {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

и назовите его вместо этого:

names = await readdirAsync('path/to/dir');
wursttheke
источник
32

Начиная с версии 10.0 , вы можете использоватьfs.Promises

Пример использования readdir

const { promises: fs } = require("fs");

async function myF() {
    let names;
    try {
        names = await fs.readdir("path/to/dir");
    } catch (e) {
        console.log("e", e);
    }
    if (names === undefined) {
        console.log("undefined");
    } else {
        console.log("First Name", names[0]);
    }
}

myF();

Пример использования readFile

const { promises: fs } = require("fs");

async function getContent(filePath, encoding = "utf-8") {
    if (!filePath) {
        throw new Error("filePath required");
    }

    return fs.readFile(filePath, { encoding });
}

(async () => {
    const content = await getContent("./package.json");

    console.log(content);
})();
DanStarns
источник
1
ExperimentalWarning: The fs.promises API is experimental
Отлично
1
@DavidP какую версию узла вы используете? 12 и выше работает нормально
DanStarns
2
Да! Абсолютно правильно - я забыл указать версию, на которой я нахожусь: v10.15.3- сообщение можно подавить. Однако, поскольку проблема все еще не решена, я подумал, что об этом стоит упомянуть.
DavidP
1
@DavidP Я имею в виду, что об этом стоит упомянуть, не поймите меня неправильно, но узел 12 сейчас находится в LTS, так что это не Biggie.
DanStarns
как именно вы используете это, скажем readFile,? Я новичок во всей этой штуке с обещаниями, и все, что я хочу сделать, это иметь функцию, getContentкоторую я могу вызывать и ожидать в различных частях моего сценария, но это оказывается очень запутанным
oldboy
8

Это версия TypeScript к вопросу. Его можно использовать после Node 11.0:

import { promises as fs } from 'fs';

async function loadMonoCounter() {
    const data = await fs.readFile('monolitic.txt', 'binary');
    return Buffer.from(data);
}
HKTonyLee
источник
5

Вот что у меня сработало:

const fsp = require('fs-promise');

(async () => {
  try {
    const names = await fsp.readdir('path/to/dir');
    console.log(names[0]);
  } catch (e) {
    console.log('error: ', e);
  }
})();

Этот код работает в узле 7.6 без Бабеля , когда флаг гармонии включен: node --harmony my-script.js. А начиная с узла 7.7 вам даже не нужен этот флаг !

fspБиблиотека включена в начале это просто promisified обертка для fsfs-ext).

Я действительно в восторге от того, что вы можете делать в node без Babel в наши дни! Нативный async/ awaitсделать написание кода таким удовольствием!

ОБНОВЛЕНИЕ 2017-06: модуль fs- prom устарел. fs-extraВместо этого используйте тот же API.

Александр Качкаев
источник
Загрузка библиотеки для этого - чистое излишество, раздутие зависимостей - это то, против чего сообщество должно решительно выступить, фактически должен появиться новый npmjs, который имеет только библиотеки с 0 зависимостями
PirateApp
5

Рекомендуется использовать пакет npm, такой как https://github.com/davetemplin/async-file , по сравнению с пользовательскими функциями. Например:

import * as fs from 'async-file';

await fs.rename('/tmp/hello', '/tmp/world');
await fs.appendFile('message.txt', 'data to append');
await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);

var stats = await fs.stat('/tmp/hello', '/tmp/world');

Другие ответы устарели

sean2078
источник
5

У меня есть небольшой вспомогательный модуль, который экспортирует обещанные версии fsфункций.

const fs = require("fs");
const {promisify} = require("util")

module.exports = {
  readdir: promisify(fs.readdir),
  readFile: promisify(fs.readFile),
  writeFile: promisify(fs.writeFile)
  // etc...
};
Григсон
источник
1

Node v14.0.0 и выше

вы можете просто сделать:

import { readdir } from "fs/promises";

так же, как вы бы импортировали из "fs"

см. этот PR для более подробной информации: https://github.com/nodejs/node/pull/31553

Konrad
источник