Jak poprawnie odczytać plik z async / await?

121

Nie mogę dowiedzieć się, jak async/ awaitdziała. Trochę to rozumiem, ale nie mogę tego zrobić.

function loadMonoCounter() {
    fs.readFileSync("monolitic.txt", "binary", async function(err, data) {
       return await new Buffer( data);
  });
}

module.exports.read = function() {
  console.log(loadMonoCounter());
};

Wiem, że mógłbym użyć readFileSync, ale jeśli to zrobię, wiem, że nigdy nie zrozumiem async/ awaiti po prostu zakopię problem.

Cel: Zadzwoń loadMonoCounter()i zwróć zawartość pliku.

Ten plik jest zwiększany za każdym razem, gdy incrementMonoCounter()jest wywoływany (przy każdym ładowaniu strony). Plik zawiera zrzut bufora w postaci binarnej i jest przechowywany na dysku SSD.

Bez względu na to, co zrobię, pojawia się błąd lub undefinedw konsoli.

Jeremy Dicaire
źródło
Czy to odpowiada na twoje pytanie? Używanie systemu plików w node.js z async /
await

Odpowiedzi:

166

Aby użyć await/ asyncpotrzebujesz metod, które zwracają obietnice. Podstawowe funkcje API nie robią tego bez opakowań takich jak promisify:

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

// Convert fs.readFile into Promise version of same    
const readFile = util.promisify(fs.readFile);

function getStuff() {
  return readFile('test');
}

// Can't use `await` outside of an async function so you need to chain
// with then()
getStuff().then(data => {
  console.log(data);
})

Uwaga: readFileSyncnie przyjmuje wywołania zwrotnego, zwraca dane lub zgłasza wyjątek. Nie otrzymujesz żądanej wartości, ponieważ funkcja, którą podasz, jest ignorowana i nie przechwytujesz rzeczywistej wartości zwracanej.

tadman
źródło
3
Dzięki, nie wiedziałem, że muszę opakować podstawowy interfejs API. Jesteś niesamowity.
Jeremy Dicaire
4
Podstawowy interfejs API poprzedza nowoczesną specyfikację Promise i przyjęcie async/ await, więc jest to niezbędny krok. Dobra wiadomość jest taka, promisifyże zwykle działa bez bałaganu.
tadman
1
To rozwiązuje problem braku możliwości normalnego wykorzystania asynchronicznego oczekiwania z FS. Dziękuję Ci za to! Uratowałeś mi tonę! Nie ma odpowiedzi, która naprawdę odnosi się do tego jak twoja.
jacobhobson
3
Również czekanie jest trochę zbędne, ponieważ można go wywnioskować. Tylko jeśli chcesz wyraźnie czekać na przykład, możesz to zrobić const file = await readFile...; return file;.
bigkahunaburger
1
@shijin Dopóki rdzeń Node API nie przełączy się na obietnice, co jest mało prawdopodobne w tym momencie, wtedy tak. Istnieją jednak opakowania NPM, które robią to za Ciebie.
tadman
152

Od czasu Node v11.0.0 obietnice FS są dostępne natywnie bez promisify:

const fs = require('fs').promises;
async function loadMonoCounter() {
    const data = await fs.readFile("monolitic.txt", "binary");
    return new Buffer(data);
}
Joel
źródło
4
żadnych dodatkowych bibliotek, czysto i prosto - to powinna być lepsza odpowiedź
Adam Bubela
2
Od 21 października 2019 r. Wersja 12 jest aktywną wersją LTS
cbronson
16
import { promises as fs } from "fs";jeśli chcesz użyć składni importu.
tr3online
21

To jest wersja odpowiedzi @ Joela w języku TypeScript. 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);
}
HKTonyLee
źródło
18

Możesz łatwo opakować polecenie readFile obietnicą taką jak ta:

async function readFile(path) {
    return new Promise((resolve, reject) => {
      fs.readFile(path, 'utf8', function (err, data) {
        if (err) {
          reject(err);
        }
        resolve(data);
      });
    });
  }

następnie użyj:

await readFile("path/to/file");
Shlomi Schwartz
źródło
Czy await nie jest używany w funkcji async?
VikasBhat
@VikasBhat Tak, powyższa linia await byłaby używana wewnątrz innej funkcji asynchronicznej, ponieważ specyfikacja tego wymaga.
whoshotdk
8

Możesz używać fs.promisesdostępnych natywnie od wersji Node 11.0.0

import fs from 'fs';

const readFile = async filePath => {
  try {
    const data = await fs.promises.readFile(filePath, 'utf8')
    return data
  }
  catch(err) {
    console.log(err)
  }
}
arnaudjnn
źródło
Jeśli chcesz korzystać tylko z obietnic, możesz zrobić coś takiegoconst fs = require('fs').promises
nathanfranke
1

Jest fs.readFileSync( path, options )metoda, która jest synchroniczna.

George Ogden
źródło