Usando o sistema de arquivos no node.js com async / waitit


129

Eu gostaria de usar async / waitit em algumas operações do sistema de arquivos. Normalmente assíncrono / espera funciona bem porque eu uso babel-plugin-syntax-async-functions.

Mas, com esse código, eu me deparo com o caso if, onde namesé indefinido:

import fs from 'fs';

async function myF() {
  let names;
  try {
    names = await fs.readdir('path/to/dir');
  } catch (e) {
    console.log('e', e);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

Quando reconstruo o código na versão infernal de retorno de chamada, tudo está bem e recebo os nomes dos arquivos. Obrigado por suas dicas.

Respostas:


139

A partir do nó 8.0.0, você pode usar isto:

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

const readdir = util.promisify(fs.readdir);

async function myF() {
  let names;
  try {
    names = await readdir('path/to/dir');
  } catch (err) {
    console.log(err);
  }
  if (names === undefined) {
    console.log('undefined');
  } else {
    console.log('First Name', names[0]);
  }
}

myF();

Consulte https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original


7
No nó v8.9.4, recebeu uma SyntaxError: Unexpected token importmensagem de erro. O node8 suporta importtoken por padrão?
makerj

9
@makerj ele está usando a nova importsintaxe. Atualmente, requer algumas transpilações. Seria ok também usar const fs = require('fs')ouconst { promisify } = require('util')
Josh Sandlin

2
Noob pergunta, mas como é {err, names} = functionchamada a sintaxe?
Qasim

6
@Qasim é chamado de atribuição de desestruturação.
precisa saber é o seguinte

1
@AlexanderZeitler Isso pode ser verdade. Não olhei para ver se esse é realmente o uso correto da desestruturação. No caso de async aguardar, acho que você faria names = await readdir('path/to/dir');e se houver um erridentificador no catchbloco. De qualquer forma, o nome da sintaxe é uma tarefa de desestruturação que foi apenas uma resposta à pergunta de Qasim.
Jaredkwright

88

Suporte nativo para assíncrono aguarda funções fs desde o Nó 11

Desde o Node.JS 11.0.0 (estável) e a versão 10.0.0 (experimental), você tem acesso aos métodos do sistema de arquivos já promisificados e pode usá-los com try catchtratamento de exceções, em vez de verificar se o valor retornado pelo retorno de chamada contém um erro.

A API é muito limpa e elegante! Basta usar o .promisesmembro do fsobjeto:

import fs from 'fs';
const fsPromises = fs.promises;

async function listDir() {
  try {
    return fsPromises.readdir('path/to/dir');
  } catch (err) {
    console.error('Error occured while reading directory!', err);
  }
}

listDir();

Esta API é estável a partir da versão 11.x acordo com o arquivo de documentação do sistema no site da Node.js
TheHanna

1
@DanStarns se você não return awaitcumprir sua promessa, o bloqueio de captura é inútil ... Eu acho que às vezes é uma boa prática aguardar antes de retornar
538ROMEO

@ 538ROMEO acabou de olhar para isso e você está certo. Obrigado por apontar isso.
DanStarns

Documentação para estes métodos alternativos: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan Takhar

86

Node.js 8.0.0

Assíncrono nativo / aguardar

Promisify

Nesta versão, você pode usar a função Node.js nativa da biblioteca util .

const fs = require('fs')
const { promisify } = require('util')

const readFileAsync = promisify(fs.readFile)
const writeFileAsync = promisify(fs.writeFile)

const run = async () => {
  const res = await readFileAsync('./data.json')
  console.log(res)
}

run()

Promise Wrapping

const fs = require('fs')

const readFile = (path, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.readFile(path, opts, (err, data) => {
      if (err) reject(err)
      else resolve(data)
    })
  })

const writeFile = (path, data, opts = 'utf8') =>
  new Promise((resolve, reject) => {
    fs.writeFile(path, data, opts, (err) => {
      if (err) reject(err)
      else resolve()
    })
  })

module.exports = {
  readFile,
  writeFile
}

...


// in some file, with imported functions above
// in async block
const run = async () => {
  const res = await readFile('./data.json')
  console.log(res)
}

run()

Adendo

Sempre use try..catchpara aguardar blocos, se você não deseja repetir a exceção superior.


Isto é estranho. Estou recebendo SyntaxError: aguardar só é válido na função assíncrona ... chorando de raiva.
Vedran Maricevic.

2
@VedranMaricevic. olhar para comentários, awaitdeve estar sempre em asyncbloco :)
dimpiax

@VedranMaricevic. Você precisa chamar isso const res = await readFile('data.json') console.log(res)de alguma função async
Jayraj

prometo embalagem fs.promisese usá-lo com async/awaité tão confuso para mim
Oldboy

O @PrimitiveNom Promise pode ser usado da maneira tradicional dentro then, catchetc.
Dimpiax 28/10/19

43

Você pode produzir o comportamento errado porque o File-Api fs.readdirnão retorna uma promessa. Leva apenas um retorno de chamada. Se você quiser usar a sintaxe async-waitit, poderá 'promisificar' a função da seguinte maneira:

function readdirAsync(path) {
  return new Promise(function (resolve, reject) {
    fs.readdir(path, function (error, result) {
      if (error) {
        reject(error);
      } else {
        resolve(result);
      }
    });
  });
}

e chame-o:

names = await readdirAsync('path/to/dir');

31

A partir da v10.0 , você pode usarfs.Promises

Exemplo usando readdir

const { promises: fs } = require("fs");

async function myF() {
    let names;
    try {
        names = await fs.readdir("path/to/dir");
    } catch (e) {
        console.log("e", e);
    }
    if (names === undefined) {
        console.log("undefined");
    } else {
        console.log("First Name", names[0]);
    }
}

myF();

Exemplo usando readFile

const { promises: fs } = require("fs");

async function getContent(filePath, encoding = "utf-8") {
    if (!filePath) {
        throw new Error("filePath required");
    }

    return fs.readFile(filePath, { encoding });
}

(async () => {
    const content = await getContent("./package.json");

    console.log(content);
})();

Funciona muito bem, mas é importante observar a questão em aberto sobre o ExperimentalWarning: The fs.promises API is experimentalaviso: github.com/pnpm/pnpm/issues/1178
DavidP

1
@DavidP qual versão do nó você está usando? 12 e acima funciona bem
DanStarns

2
Sim! Absolutamente correto - eu esqueci de indicar a versão em que estou: v10.15.3- é possível suprimir a mensagem. No entanto, com o problema ainda em aberto, achei que vale a pena mencionar.
DavidP

1
@DavidP Quero dizer que vale a pena mencionar, não me interpretem mal, mas o nó 12 está no LTS agora, portanto não é um Biggie.
DanStarns 12/12/19

como exatamente você usa isso com, digamos readFile,? im novo para essa coisa toda de promessas, e tudo que eu quero fazer é ter uma função getContentque eu posso chamar e aguardar em várias partes durante todo o meu script, mas isso está provando muito confuso
Oldboy

8

Esta é a versão do TypeScript para a pergunta. É utilizável após o Nó 11.0:

import { promises as fs } from 'fs';

async function loadMonoCounter() {
    const data = await fs.readFile('monolitic.txt', 'binary');
    return Buffer.from(data);
}

5

Aqui está o que funcionou para mim:

const fsp = require('fs-promise');

(async () => {
  try {
    const names = await fsp.readdir('path/to/dir');
    console.log(names[0]);
  } catch (e) {
    console.log('error: ', e);
  }
})();

Esse código funciona no nó de 7.6 sem babel quando bandeira harmonia está ativada: node --harmony my-script.js. E começando com o nó 7.7, você nem precisa desse sinalizador !

A fspbiblioteca incluída no começo é apenas um wrapper promisificado para fs(e fs-ext).

Estou realmente empolgado com o que você pode fazer no nó sem o babel hoje em dia! Nativo async/ awaittornar o código de escrita um prazer!

ATUALIZAÇÃO 2017-06: o módulo fs-promessa foi descontinuado. Use em fs-extravez disso com a mesma API.


Baixando uma biblioteca para isso é puro exagero, inchaço dependência é algo que a comunidade deve ser fortemente contra, realmente uma nova npmjs deve entrar em fazer que só tem libs com 0 dependências
PirateApp

5

Recomenda o uso de um pacote npm, como https://github.com/davetemplin/async-file , em comparação com as funções personalizadas. Por exemplo:

import * as fs from 'async-file';

await fs.rename('/tmp/hello', '/tmp/world');
await fs.appendFile('message.txt', 'data to append');
await fs.access('/etc/passd', fs.constants.R_OK | fs.constants.W_OK);

var stats = await fs.stat('/tmp/hello', '/tmp/world');

Outras respostas estão desatualizadas


5

Eu tenho esse pequeno módulo auxiliar que exporta versões promissificadas de fsfunções

const fs = require("fs");
const {promisify} = require("util")

module.exports = {
  readdir: promisify(fs.readdir),
  readFile: promisify(fs.readFile),
  writeFile: promisify(fs.writeFile)
  // etc...
};

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.