Verwenden des Dateisystems in node.js mit async / await

129

Ich möchte async / await für einige Dateisystemoperationen verwenden. Normalerweise funktioniert async / await einwandfrei, da ich es verwende babel-plugin-syntax-async-functions.

Aber mit diesem Code stoße ich auf den if-Fall, in dem namesundefiniert ist:

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

Wenn ich den Code in die Callback-Hell-Version umbaue, ist alles in Ordnung und ich erhalte die Dateinamen. Vielen Dank für Ihre Hinweise.

Quellenangeber
quelle

Antworten:

139

Ab Knoten 8.0.0 können Sie Folgendes verwenden:

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

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

Azbykov
quelle
7
In Knoten v8.9.4 wurde eine SyntaxError: Unexpected token importFehlermeldung angezeigt . Unterstützt importNode8 standardmäßig Token?
Makerj
9
@makerj er benutzt die neue importSyntax. Es erfordert derzeit einige Transpiling. Wäre in Ordnung, auch zu verwenden const fs = require('fs')oderconst { promisify } = require('util')
Josh Sandlin
2
Noob Frage, aber wie heißt die {err, names} = functionSyntax?
Qasim
6
@Qasim heißt Destrukturierungszuweisung.
Jaredkwright
1
@AlexanderZeitler Das mag wahr sein. Ich habe nicht nachgesehen, ob dies tatsächlich eine korrekte Verwendung der Destrukturierung ist. Im Falle von Async warten Sie, ich denke, Sie würden es einfach tun names = await readdir('path/to/dir');und wenn es ein errHandle im catchBlock gibt. In jedem Fall ist der Name der Syntax eine Destrukturierungszuweisung, die nur eine Antwort auf Qasims Frage war.
Jaredkwright
88

Die native Unterstützung für Async wartet seit Knoten 11 auf fs-Funktionen

Seit Node.JS 11.0.0 (stabil) und Version 10.0.0 (experimentell) haben Sie Zugriff auf Dateisystemmethoden, die bereits versprochen wurden, und Sie können sie mit try catchAusnahmebehandlung verwenden, anstatt zu überprüfen, ob der zurückgegebene Wert des Rückrufs enthält ein Fehler.

Die API ist sehr sauber und elegant! Verwenden Sie einfach ein .promisesMitglied des fsObjekts:

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();
bman
quelle
Diese API ist ab Version 11.x gemäß der Dateisystemdokumentation auf der Node.js-Site
stabil
1
@DanStarns Wenn Sie return awaitIhr Versprechen nicht einhalten, nützt der Fangblock nichts ... Ich denke, es ist manchmal eine gute Praxis, vor der Rückkehr abzuwarten
538ROMEO
@ 538ROMEO hat sich das und dein Recht angesehen. Vielen Dank für den Hinweis.
DanStarns
Dokumentation für diese alternativen Methoden: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan Takhar
86

Node.js 8.0.0

Native async / warten

Versprich es mir

Ab dieser Version können Sie die native Node.js-Funktion aus der util- Bibliothek verwenden.

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


Promise Wrapping

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

Rat

Verwenden try..catchSie diese Option immer für Warteblöcke, wenn Sie die obere Ausnahme nicht erneut auslösen möchten.

Dimpiax
quelle
Das ist merkwürdig. Ich erhalte SyntaxError: Warten ist nur in der asynchronen Funktion gültig ... vor Wut weinen.
Vedran Maricevic.
2
@VedranMaricevic. schau dir die Kommentare an, awaitmuss immer im asyncBlock sein :)
dimpiax
@VedranMaricevic. Sie müssen das const res = await readFile('data.json') console.log(res)in einer asynchronen Funktion
aufrufen
Versprechen, es zu verpacken fs.promisesund damit zu benutzen, async/awaitist für mich so verwirrend
Oldboy
@PrimitiveNom Versprechen kann innerhalb in der traditionellen Art und Weise verwendet werden then, catchusw. Wo sind async / await ist modern Verhalten fließen.
Dimpiax
43

Möglicherweise führen Sie zu einem falschen Verhalten, da die Datei-API fs.readdirkein Versprechen zurückgibt. Es dauert nur einen Rückruf. Wenn Sie mit der Async-Wait-Syntax arbeiten möchten, können Sie die Funktion wie folgt "versprechen":

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

und nenne es stattdessen:

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

Ab Version 10.0 können Sie verwendenfs.Promises

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

Beispiel mit 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
quelle
Funktioniert hervorragend, ist aber wichtig, um das offene Problem bezüglich der ExperimentalWarning: The fs.promises API is experimentalWarnung zu beachten : github.com/pnpm/pnpm/issues/1178
DavidP
1
@DavidP Welche Version des Knotens verwenden Sie? 12 und höher funktioniert gut
DanStarns
2
Ja! Absolut korrekt - Ich habe es versäumt, die Version anzugeben, auf der ich mich befinde: v10.15.3- Es ist möglich, die Nachricht zu unterdrücken. Da das Thema jedoch noch offen ist, hielt ich es für erwähnenswert.
DavidP
1
@ DavidP Ich meine, es ist eine Erwähnung wert, versteh mich nicht falsch, aber Knoten 12 ist jetzt in LTS, also ist es kein Biggie.
DanStarns
Wie genau verwenden Sie dies beispielsweise readFile? Ich bin neu in dieser ganzen Versprechenssache, und alles, was ich tun möchte, ist eine Funktion zu haben getContent, die ich in verschiedenen Teilen meines Skripts aufrufen und abwarten kann, aber dies erweist sich als sehr verwirrend
Oldboy
8

Dies ist die TypeScript-Version der Frage. Es kann nach Knoten 11.0 verwendet werden:

import { promises as fs } from 'fs';

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

Folgendes hat bei mir funktioniert:

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

Dieser Code funktioniert in Knoten 7.6 ohne Babel, wenn das Harmony-Flag aktiviert ist : node --harmony my-script.js. Und ab Knoten 7.7 brauchen Sie nicht einmal dieses Flag !

Die fspam Anfang enthaltene Bibliothek ist nur ein vielversprechender Wrapper für fs(und fs-ext).

Ich bin wirklich gespannt, was man heutzutage in Node ohne Babel machen kann! Native async/ awaitmachen das Schreiben von Code zu einem Vergnügen!

UPDATE 2017-06: Das Modul fs-versprechen ist veraltet. Verwenden Sie fs-extrastattdessen mit derselben API.

Alexander Kachkaev
quelle
Das Herunterladen einer Bibliothek dafür ist ein reiner Overkill. Das Aufblähen von Abhängigkeiten ist etwas, gegen das die Community stark sein sollte. Tatsächlich sollte ein neues npmjs erstellt werden, das nur
Bibliotheken
5

Empfehlen Sie die Verwendung eines npm-Pakets wie https://github.com/davetemplin/async-file im Vergleich zu benutzerdefinierten Funktionen. Beispielsweise:

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

Andere Antworten sind veraltet

sean2078
quelle
5

Ich habe dieses kleine Hilfsmodul , das vielversprechende Versionen von fsFunktionen exportiert

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

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

Knoten v14.0.0 und höher

Sie können einfach tun:

import { readdir } from "fs/promises";

genau wie Sie aus importieren würden "fs"

Weitere Informationen finden Sie in dieser PR: https://github.com/nodejs/node/pull/31553

Konrad
quelle