Korzystanie z systemu plików w node.js z async / await


130

Chciałbym używać async / await z niektórymi operacjami na systemie plików. Zwykle async / await działa dobrze, ponieważ używam babel-plugin-syntax-async-functions.

Ale z tym kodem napotykam przypadek if, w którym namesjest niezdefiniowany:

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

Kiedy przebudowuję kod na wersję callback hell, wszystko jest w porządku i otrzymuję nazwy plików. Dzięki za podpowiedzi.

Odpowiedzi:


140

Począwszy od węzła 8.0.0, możesz użyć tego:

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

Zobacz https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original


7
W węźle w wersji 8.9.4 pojawił się SyntaxError: Unexpected token importkomunikat o błędzie. czy node8 domyślnie obsługuje importtoken?
— makerj

9
@makerj używa nowej importskładni. Obecnie wymaga transpilacji. Byłoby w porządku, aby również użyć const fs = require('fs')lubconst { promisify } = require('util')
— Josh Sandlin

2
Noob pytanie, ale jak {err, names} = functionnazywa się składnia?
— Qasim,

6
@Qasim nazywa się to przydziałem destrukturyzacji.
— jaredkwright

1
@AlexanderZeitler To może być prawda. Nie sprawdzałem, czy to właściwie jest właściwe użycie destrukturyzacji. W przypadku oczekiwania async myślę, że po prostu zrobiłbyś names = await readdir('path/to/dir');i jeśli jest erruchwyt w catchbloku. Tak czy inaczej, nazwa składni to destrukturyzujące przypisanie, które było właśnie odpowiedzią na pytanie Qasima.
— jaredkwright

88

Natywne wsparcie dla funkcji async await fs od Node 11

Od wersji Node.JS 11.0.0 (stabilna) i 10.0.0 (eksperymentalna) masz dostęp do metod systemu plików, które są już obiecane i możesz ich używać z try catchobsługą wyjątków zamiast sprawdzać, czy zwrócona wartość wywołania zwrotnego zawiera błąd.

Interfejs API jest bardzo czysty i elegancki! Po prostu użyj .promisesczłonka fsobiektu:

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

Ten interfejs API jest stabilny od wersji 11.x zgodnie z dokumentacją systemu plików w witrynie Node.js
— TheHanna

1
@DanStarns, jeśli nie return awaitobiecasz, blokada na nic się nie przyda ... Myślę, że czasami dobrą praktyką jest
— zaczekanie

@ 538ROMEO właśnie zajrzał do tego i po prawej stronie. Dzięki za wskazanie tego.
— DanStarns

Dokumentacja alternatywnych metod: nodejs.org/api/fs.html#fs_fs_promises_api
— Jeevan Takhar

87

Node.js 8.0.0

Natywny async / await

Promisify

Od tej wersji można używać natywnej funkcji Node.js z biblioteki 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()

Pakowanie obietnic

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

Rada

Zawsze używaj try..catchdla bloków await, jeśli nie chcesz ponownie wyrzucać wyjątku górnego.


To jest dziwne. Otrzymuję błąd SyntaxError: await działa tylko w funkcji async ... płacze z wściekłości.
— Vedran Maricevic.

2
@VedranMaricevic. spójrz na komentarze, awaitzawsze musi być w asyncbloku :)
— dimpiax

@VedranMaricevic. Musisz to wywołać const res = await readFile('data.json') console.log(res)w jakiejś funkcji asynchronicznej
— Jayraj,

Obietnica zawijania fs.promisesi używania jej async/awaitjest dla mnie tak myląca
— oldboy

@PrimitiveNom Promesa może być stosowany w tradycyjny sposób wewnątrz then, catchitd. Gdzie są asynchroniczne / Oczekujcie jest nowoczesny przepływ zachowanie.
— dimpiax

43

Możesz uzyskać niewłaściwe zachowanie, ponieważ File-Api fs.readdirnie zwraca obietnicy. Wystarczy oddzwonić. Jeśli chcesz korzystać ze składni async-await, możesz `` obiecać '' funkcję w następujący sposób:

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

i nazwij to zamiast tego:

names = await readdirAsync('path/to/dir');

32

Od wersji 10.0 możesz używaćfs.Promises

Przykład użycia 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();

Przykład użycia 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);
})();

1
Działa świetnie, ale ważne jest, aby zwrócić uwagę na otwarty problem dotyczący ExperimentalWarning: The fs.promises API is experimentalostrzeżenia: github.com/pnpm/pnpm/issues/1178
— DavidP,

1
@DavidP jakiej wersji węzła używasz? 12 i
— nowsze

2
Tak! Jak najbardziej poprawne - zaniedbałem podanie wersji na której jestem: v10.15.3- można ukryć komunikat. Jednak mając wciąż otwarty problem, pomyślałem, że warto o nim wspomnieć.
— DavidP

1
@DavidP Chodzi mi o to, że warto wspomnieć, nie zrozum mnie źle, ale węzeł 12 jest teraz w LTS, więc nie jest to Biggie.
— DanStarns,

jak dokładnie tego używasz, powiedzmy readFile? Jestem nowy w tej całej sprawie obietnic i wszystko, czego chcę, to mieć funkcję, getContentktórą mogę wywołać i czekać w różnych częściach mojego skryptu, ale to okazuje się bardzo zagmatwane
— oldboy

8

To jest wersja TypeScript pytania. Można go używać po Węzeł 11.0:

import { promises as fs } from 'fs';

async function loadMonoCounter() {
    const data = await fs.readFile('monolitic.txt', 'binary');
    return Buffer.from(data);
}

5

Oto, co zadziałało dla mnie:

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

Ten kod działa w węźle 7.6 bez babel gdy flaga harmonia jest włączona: node --harmony my-script.js. Począwszy od węzła 7.7, nie potrzebujesz nawet tej flagi !

fspBiblioteka zawarte na początku jest tylko promisified wrapper dla fs(i fs-ext).

Jestem naprawdę podekscytowany tym, co możesz teraz zrobić w Node bez Babel! Native async/ awaitsprawiają, że pisanie kodu to taka przyjemność!

UPDATE 2017-06: moduł fs-promise został wycofany. fs-extraZamiast tego użyj tego samego interfejsu API.


Pobieranie biblioteki w tym celu jest czystą przesadą, nadęty zależności to coś, czemu społeczność powinna być zdecydowanie przeciw, w rzeczywistości nowy npmj powinien pojawić się, który ma tylko biblioteki z
— zerowymi

5

Zaleca się używanie pakietu npm, takiego jak https://github.com/davetemplin/async-file , w porównaniu z funkcjami niestandardowymi. Na przykład:

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');

Inne odpowiedzi są nieaktualne


5

Mam ten mały moduł pomocniczy, który eksportuje obiecane wersje fsfunkcji

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

module.exports = {
  readdir: promisify(fs.readdir),
  readFile: promisify(fs.readFile),
  writeFile: promisify(fs.writeFile)
  // etc...
};

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.