Menu

fs/promises no Node.js: arquivos com async/await

fs/promises é a versão com promises do módulo fs do Node: readFile, writeFile, readdir, stat, mkdir, rm e rename retornam promises em que você dá await, e os erros são tratados com try/catch.

Esta página tem editores executáveis. Edite, execute e veja a saída na hora.

fs/promises é a versão baseada em promises do módulo fs do Node. As funções dele (readFile, writeFile, readdir, stat, mkdir, rm, rename e as demais) retornam uma promise em vez de receber um callback, então você dá await nelas e trata erros com try/catch. Importe com require('fs/promises') ou import fs from 'fs/promises'.

Ele imprime [ 'write docs', 'ship it' ]: cada await espera uma operação antes de a próxima linha rodar, então o append sempre acontece depois da primeira escrita. Em um ES module o await funciona no nível superior; em CommonJS, coloque o código dentro de uma função async.

As funções que você mais vai usar

Os nomes são os mesmos da API de callbacks, sem o callback. As páginas de readFile e writeFile cobrem essas duas em detalhe.

FunçãoFazResolve com
readFile(path, 'utf8')Lê um arquivo inteiroO conteúdo
writeFile(path, data)Cria ou substitui um arquivoundefined
appendFile(path, data)Adiciona no finalundefined
readdir(path)Lista uma pastaUm array de nomes
stat(path)Tamanho, datas, arquivo ou pastaUm objeto Stats
mkdir(path, { recursive: true })Cria pastasA primeira pasta criada, ou undefined
rename(from, to)Move ou renomeiaundefined
rm(path, { recursive: true, force: true })Apaga um arquivo ou pastaundefined

Listar uma pasta com readdir e stat

readdir retorna só os nomes. Chame stat em cada um para pegar o tamanho ou diferenciar arquivos de pastas, ou passe { withFileTypes: true } para receber entradas que já sabem o próprio tipo.

O loop imprime README.md 6 bytes, notes.txt 3 bytes e src folder. stat rejeita com ENOENT para um caminho que não existe, o que faz dele uma verificação rápida de existência quando você também quer os detalhes.

Renomear, mover e apagar

rename tanto renomeia quanto move um arquivo (dentro do mesmo disco). rm apaga; ele precisa de recursive: true para uma pasta com conteúdo, e de force: true para não falhar quando o caminho já não existe.

O arquivo sai de drafts para published com o nome novo, e depois do rm a pasta blog lista só published. Tire o force: true do segundo rm e ele rejeita com ENOENT.

Tratar erros com try/catch

Uma operação que falha rejeita a promise. Com await, essa rejeição vira uma exceção que você captura como qualquer outra. O code do erro diz o que deu errado.

A primeira chamada não encontra arquivo e retorna []; a segunda imprime Ada. Códigos para conhecer: ENOENT (arquivo não existe), EEXIST (já existe), EISDIR (uma pasta onde se esperava um arquivo), ENOTEMPTY (a pasta ainda tem conteúdo) e EACCES (sem permissão).

Uma promise em que você não dá await nem .catch() vira uma unhandled rejection quando falha, e o Node termina com erro. Dê await em toda chamada, ou retorne a promise para quem chamou e vai dar await.

Ler vários arquivos de uma vez com Promise.all

Um loop for com await lê os arquivos um depois do outro. Quando as leituras não dependem umas das outras, inicie todas e espere uma vez com Promise.all: o disco trabalha nelas ao mesmo tempo.

Promise.all mantém a ordem do array de entrada, então o resultado é [ 'alpha', 'beta', 'gamma' ] independentemente de qual leitura terminou primeiro, e o tamanho juntado é 14. Troque 'b.txt' no array por 'x.txt' e o await inteiro rejeita com ENOENT.

Se você precisa de todos os resultados mesmo quando algumas leituras falham, use Promise.allSettled; a página de promises cobre os dois.

Erros comuns

Confundir os dois módulos. require('fs').readFile quer um callback; require('fs/promises').readFile retorna uma promise. Chamar a versão de callback sem callback lança The "cb" argument must be of type function.

Esquecer o await. const text = fs.readFile(...) é uma promise pendente, e text.split lança text.split is not a function.

Escritas em paralelo no mesmo arquivo. Promise.all com várias escritas no mesmo caminho pode deixar o conteúdo misturado. Rode essas em sequência.

Perguntas frequentes

Qual a diferença entre fs.promises e fs/promises?

Nenhuma. require('fs').promises e require('fs/promises') são o mesmo objeto. O caminho fs/promises (ou node:fs/promises) é o jeito comum de importar hoje.

Existe existsSync no fs/promises?

Não. A API com promises tem access, que rejeita quando o caminho não existe, mas o padrão usual é chamar a operação de que você precisa e tratar ENOENT. O existsSync do módulo fs comum continua funcionando para verificações rápidas.

O fs/promises é mais lento que a API de callbacks?

Um pouco, pelo custo das promises, mas é desprezível perto do tempo de disco. Escolha pela legibilidade; a API com promises é a que a maior parte do código novo usa.

Como transformo uma função fs de callback em promise?

Raramente é preciso, já que o fs/promises cobre as funções de arquivo. Para outras APIs de callback, util.promisify(fn) devolve uma versão que retorna promise.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

Começar