fs/promises est la version à promesses du module fs de Node. Ses fonctions (readFile, writeFile, readdir, stat, mkdir, rm, rename et les autres) renvoient une promesse au lieu de prendre un callback, donc vous les attendez avec await et interceptez les erreurs avec try/catch. Importez-le avec require('fs/promises') ou import fs from 'fs/promises'.
Le programme affiche [ 'write docs', 'ship it' ] : chaque await attend la fin d'une opération avant que la ligne suivante s'exécute, donc l'ajout se fait toujours après la première écriture. Dans un module ES, await fonctionne au niveau supérieur ; en CommonJS, placez le code dans une fonction async.
Les fonctions les plus utilisées
Les noms sont ceux de l'API à callbacks, sans le callback. Les pages readFile et writeFile détaillent ces deux-là.
| Fonction | Rôle | Se résout en |
|---|---|---|
readFile(path, 'utf8') | Lit un fichier entier | Le contenu |
writeFile(path, data) | Crée ou remplace un fichier | undefined |
appendFile(path, data) | Ajoute à la fin | undefined |
readdir(path) | Liste un dossier | Un tableau de noms |
stat(path) | Taille, dates, fichier ou dossier | Un objet Stats |
mkdir(path, { recursive: true }) | Crée des dossiers | Le premier dossier créé, ou undefined |
rename(from, to) | Déplace ou renomme | undefined |
rm(path, { recursive: true, force: true }) | Supprime un fichier ou un dossier | undefined |
Lister un dossier avec readdir et stat
readdir ne renvoie que des noms. Appelez stat sur chacun pour obtenir sa taille ou distinguer fichiers et dossiers, ou passez { withFileTypes: true } pour obtenir des entrées qui connaissent déjà leur type.
La boucle affiche README.md 6 bytes, notes.txt 3 bytes et src folder. stat rejette avec ENOENT pour un chemin qui n'existe pas, ce qui en fait un test d'existence rapide quand vous voulez aussi les détails.
Renommer, déplacer et supprimer
rename renomme et déplace un fichier (sur le même disque). rm supprime ; il lui faut recursive: true pour un dossier non vide, et force: true pour ne pas échouer quand le chemin a déjà disparu.
Le fichier passe de drafts à published sous son nouveau nom, et après rm le dossier blog ne contient plus que published. Retirez force: true du second rm et il rejette avec ENOENT.
Gérer les erreurs avec try/catch
Une opération qui échoue rejette sa promesse. Avec await, ce rejet devient une exception que vous interceptez comme n'importe quelle autre. Le code de l'erreur indique ce qui s'est mal passé.
Le premier appel ne trouve pas le fichier et renvoie [] ; le second affiche Ada. Les codes à connaître : ENOENT (fichier inexistant), EEXIST (existe déjà), EISDIR (un dossier là où un fichier était attendu), ENOTEMPTY (un dossier contient encore des éléments) et EACCES (permission refusée).
Une promesse que vous n'attendez pas avec await et sans .catch() devient un rejet non géré si elle échoue, et Node se termine avec une erreur. Attendez chaque appel, ou renvoyez sa promesse à un appelant qui le fait.
Lire plusieurs fichiers à la fois avec Promise.all
Une boucle for avec await lit les fichiers l'un après l'autre. Quand les lectures ne dépendent pas les unes des autres, lancez-les toutes et attendez une seule fois avec Promise.all : le disque les traite en même temps.
Promise.all conserve l'ordre du tableau d'entrée, donc le résultat est [ 'alpha', 'beta', 'gamma' ] quelle que soit la lecture terminée en premier, et la longueur concaténée est 14. Renommez 'b.txt' en 'x.txt' dans le tableau et tout l'await rejette avec ENOENT.
Si vous avez besoin de chaque résultat même quand certaines lectures échouent, utilisez Promise.allSettled ; la page sur les promesses couvre les deux.
Erreurs fréquentes
Confondre les deux modules. require('fs').readFile attend un callback ; require('fs/promises').readFile renvoie une promesse. Appeler la version à callback sans callback lève The "cb" argument must be of type function.
Oublier await. const text = fs.readFile(...) est une promesse en attente, et text.split lève text.split is not a function.
Écrire en parallèle dans un même fichier. Promise.all avec plusieurs écritures sur le même chemin peut laisser un contenu mélangé. Exécutez-les à la suite.
Questions fréquentes
Quelle différence entre fs.promises et fs/promises ?
Aucune. require('fs').promises et require('fs/promises') sont le même objet. Le chemin fs/promises (ou node:fs/promises) est la façon habituelle de l'importer aujourd'hui.
Existe-t-il un existsSync dans fs/promises ?
Non. L'API à promesses a access, qui rejette quand le chemin n'existe pas, mais l'approche habituelle est d'appeler directement l'opération voulue et d'intercepter ENOENT. existsSync du module fs classique marche toujours pour une vérification rapide.
fs/promises est-il plus lent que l'API à callbacks ?
Un peu, à cause du coût des promesses, mais c'est négligeable comparé au temps disque. Choisissez selon la lisibilité ; l'API à promesses est celle qu'utilise la majorité du code récent.
Comment transformer une fonction fs à callback en promesse ?
C'est rarement nécessaire, puisque fs/promises couvre les fonctions de fichiers. Pour d'autres API à callbacks, util.promisify(fn) renvoie une version qui retourne une promesse.