Menu

fs/promises Node.js : système de fichiers avec async/await

fs/promises est la version à promesses du module fs de Node : readFile, writeFile, readdir, stat, mkdir, rm et rename renvoient tous des promesses que vous attendez avec await, et les erreurs s'interceptent avec try/catch.

Cette page contient des éditeurs exécutables. Modifiez, exécutez et voyez la sortie instantanément.

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à.

FonctionRôleSe résout en
readFile(path, 'utf8')Lit un fichier entierLe contenu
writeFile(path, data)Crée ou remplace un fichierundefined
appendFile(path, data)Ajoute à la finundefined
readdir(path)Liste un dossierUn tableau de noms
stat(path)Taille, dates, fichier ou dossierUn objet Stats
mkdir(path, { recursive: true })Crée des dossiersLe premier dossier créé, ou undefined
rename(from, to)Déplace ou renommeundefined
rm(path, { recursive: true, force: true })Supprime un fichier ou un dossierundefined

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.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

Commencer