Como ler o arquivo com async / await corretamente?

120

Não consigo descobrir como async/ awaitfunciona. Eu entendo um pouco, mas não consigo fazer funcionar.

function loadMonoCounter() {
    fs.readFileSync("monolitic.txt", "binary", async function(err, data) {
       return await new Buffer( data);
  });
}

module.exports.read = function() {
  console.log(loadMonoCounter());
};

Sei que poderia usar readFileSync, mas se usar , sei que nunca vou entender async/ awaite vou simplesmente enterrar o problema.

Objetivo: Ligar loadMonoCounter()e retornar o conteúdo de um arquivo.

Esse arquivo é incrementado toda vez que incrementMonoCounter()é chamado (a cada carregamento de página). O arquivo contém o dump de um buffer em binário e é armazenado em um SSD.

Não importa o que eu faça, recebo um erro undefinedno console.

Jeremy Dicaire
fonte
Isso responde sua pergunta? Usando o sistema de arquivos em node.js com async /
await

Respostas:

165

Para usar await/ asyncvocê precisa de métodos que retornam promessas. As funções principais da API não fazem isso sem wrappers como promisify:

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

// Convert fs.readFile into Promise version of same    
const readFile = util.promisify(fs.readFile);

function getStuff() {
  return readFile('test');
}

// Can't use `await` outside of an async function so you need to chain
// with then()
getStuff().then(data => {
  console.log(data);
})

Como uma observação, readFileSyncnão recebe um retorno de chamada, ele retorna os dados ou lança uma exceção. Você não está obtendo o valor desejado porque a função fornecida é ignorada e você não está capturando o valor de retorno real.

Tadman
fonte
3
Obrigado, eu não sabia que precisava envolver a API principal. Você é demais.
Jeremy Dicaire
4
A API principal é anterior à especificação moderna da Promise e à adoção de async/ await, portanto, essa é uma etapa necessária. A boa notícia é que promisifygeralmente funciona sem problemas.
tadman
1
Isso lida com a bagunça de não ser capaz de aproveitar async-await com FS normalmente. Obrigado por isso! Você me salvou uma tonelada! Não há resposta que realmente trate disso como a sua.
jacobhobson
3
Também await é meio redundante, pois pode ser inferido. Somente se você quiser explicitamente esperar no exemplo, você pode fazer const file = await readFile...; return file;.
bigkahunaburger
1
@shijin Até que a API principal do Node mude para promessas, o que é improvável neste momento, então sim. No entanto, existem wrappers NPM que fazem isso por você.
tadman
149

Desde Node v11.0.0 fs promessas estão disponíveis nativamente sem promisify:

const fs = require('fs').promises;
async function loadMonoCounter() {
    const data = await fs.readFile("monolitic.txt", "binary");
    return new Buffer(data);
}
Joel
fonte
4
sem libs adicionais, limpo e simples - esta deve ser a resposta preferível
Adam Bubela 09/07/19
2
Desde 21 de outubro de 2019, a v12 é uma versão LTS ativa
cbronson
16
import { promises as fs } from "fs";se você quiser usar a sintaxe de importação.
tr3online
21

Esta é a versão TypeScript da resposta de @Joel. É 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);
}
HKTonyLee
fonte
18

Você pode facilmente envolver o comando readFile com uma promessa como esta:

async function readFile(path) {
    return new Promise((resolve, reject) => {
      fs.readFile(path, 'utf8', function (err, data) {
        if (err) {
          reject(err);
        }
        resolve(data);
      });
    });
  }

então use:

await readFile("path/to/file");
Shlomi Schwartz
fonte
O await não é usado na função assíncrona?
VikasBhat
@VikasBhat Sim, a linha de espera acima seria usada dentro de outra função assíncrona, pois a especificação exige que seja assim.
whoshotdk
8

Você pode usar o fs.promisesdisponível nativamente desde o Node v11.0.0

import fs from 'fs';

const readFile = async filePath => {
  try {
    const data = await fs.promises.readFile(filePath, 'utf8')
    return data
  }
  catch(err) {
    console.log(err)
  }
}
Arnaudjnn
fonte
Se você só quiser usar promessas, pode fazer algo comoconst fs = require('fs').promises
nathanfranke
1

Existe um fs.readFileSync( path, options )método que é síncrono.

George Ogden
fonte