fs/promises to wersja modułu fs w Node oparta na promise. Jego funkcje (readFile, writeFile, readdir, stat, mkdir, rm, rename i pozostałe) zwracają promise zamiast przyjmować callback, więc czekasz na nie przez await, a błędy łapiesz przez try/catch. Zaimportuj go przez require('fs/promises') albo import fs from 'fs/promises'.
Wypisuje [ 'write docs', 'ship it' ]: każde await czeka na jedną operację, zanim wykona się następna linia, więc dopisanie zawsze następuje po pierwszym zapisie. W module ES await działa na najwyższym poziomie; w CommonJS opakuj kod w funkcję async.
Funkcje, których będziesz używać najczęściej
Nazwy są takie jak w API z callbackami, tylko bez callbacku. Strony o readFile i writeFile opisują te dwie dokładnie.
| Funkcja | Co robi | Rozwiązuje się do |
|---|---|---|
readFile(path, 'utf8') | Czyta cały plik | Zawartości |
writeFile(path, data) | Tworzy albo zastępuje plik | undefined |
appendFile(path, data) | Dopisuje na końcu | undefined |
readdir(path) | Listuje folder | Tablicy nazw |
stat(path) | Rozmiar, daty, plik czy folder | Obiektu Stats |
mkdir(path, { recursive: true }) | Tworzy foldery | Pierwszego utworzonego folderu albo undefined |
rename(from, to) | Przenosi albo zmienia nazwę | undefined |
rm(path, { recursive: true, force: true }) | Usuwa plik albo folder | undefined |
Listowanie folderu przez readdir i stat
readdir zwraca tylko nazwy. Wywołaj stat dla każdej, żeby poznać rozmiar albo odróżnić pliki od folderów, albo przekaż { withFileTypes: true }, żeby dostać wpisy, które już znają swój typ.
Pętla wypisuje README.md 6 bytes, notes.txt 3 bytes i src folder. stat odrzuca z ENOENT dla ścieżki, która nie istnieje, więc to szybkie sprawdzenie istnienia, gdy chcesz też poznać szczegóły.
Zmiana nazwy, przenoszenie i usuwanie
rename zarówno zmienia nazwę, jak i przenosi plik (w obrębie tego samego dysku). rm usuwa; dla folderu z zawartością potrzebuje recursive: true, a force: true, żeby nie zawieść, gdy ścieżki już nie ma.
Plik przenosi się z drafts do published pod nową nazwą, a po rm folder blog zawiera tylko published. Usuń force: true z drugiego rm, a odrzuci z ENOENT.
Obsługa błędów przez try/catch
Nieudana operacja odrzuca swój promise. Z await to odrzucenie staje się wyjątkiem, który łapiesz jak każdy inny. code błędu mówi, co poszło nie tak.
Pierwsze wywołanie nie znajduje pliku i zwraca []; drugie wypisuje Ada. Kody, które warto znać: ENOENT (nie ma takiego pliku), EEXIST (już istnieje), EISDIR (folder tam, gdzie oczekiwano pliku), ENOTEMPTY (folder nadal ma zawartość) i EACCES (brak uprawnień).
Promise, na który nie czekasz przez await i nie dodasz .catch(), przy błędzie staje się nieobsłużonym odrzuceniem, a Node kończy działanie z błędem. Czekaj na każde wywołanie albo zwracaj jego promise do kodu, który to robi.
Czytanie kilku plików naraz przez Promise.all
Pętla for z await czyta pliki jeden po drugim. Gdy odczyty od siebie nie zależą, uruchom wszystkie i poczekaj raz przez Promise.all: dysk pracuje nad nimi równocześnie.
Promise.all zachowuje kolejność tablicy wejściowej, więc wynik to [ 'alpha', 'beta', 'gamma' ] niezależnie od tego, który odczyt skończył się pierwszy, a łączna długość to 14. Zmień w tablicy 'b.txt' na 'x.txt', a całe await odrzuci z ENOENT.
Jeśli potrzebujesz każdego wyniku, nawet gdy niektóre odczyty się nie udadzą, użyj Promise.allSettled; strona o promise opisuje oba.
Częste błędy
Mylenie dwóch modułów. require('fs').readFile oczekuje callbacku; require('fs/promises').readFile zwraca promise. Wywołanie wersji z callbackiem bez callbacku rzuca The "cb" argument must be of type function.
Zapominanie o await. const text = fs.readFile(...) to oczekujący promise, a text.split rzuca text.split is not a function.
Równoległe zapisy do jednego pliku. Promise.all z kilkoma zapisami do tej samej ścieżki może zostawić wymieszaną zawartość. Wykonuj je po kolei.
Najczęściej zadawane pytania
Czym różni się fs.promises od fs/promises?
Niczym. require('fs').promises i require('fs/promises') to ten sam obiekt. Ścieżka fs/promises (albo node:fs/promises) to dziś typowy sposób importu.
Czy w fs/promises jest existsSync?
Nie. API z promise ma access, które odrzuca, gdy ścieżki nie ma, ale typowy wzorzec to wywołać potrzebną operację i złapać ENOENT. existsSync ze zwykłego modułu fs nadal działa do szybkich sprawdzeń.
Czy fs/promises jest wolniejsze od API z callbackami?
Trochę, przez narzut promise, ale przy czasie dostępu do dysku to pomijalne. Wybieraj według czytelności; API z promise to to, którego używa większość nowego kodu.
Jak zamienić funkcję fs z callbackiem na promise?
Rzadko jest to potrzebne, bo fs/promises obejmuje funkcje plikowe. Dla innych API z callbackami util.promisify(fn) zwraca wersję, która zwraca promise.