¿Cómo leer correctamente un archivo con async / await?

120

No puedo entender cómo funciona async/ await. Lo entiendo un poco, pero no puedo hacer que funcione.

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

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

Sé que podría utilizar readFileSync, pero si lo hago, sé que nunca voy a entender async/ awaity voy a enterrar el asunto.

Objetivo: llamar loadMonoCounter()y devolver el contenido de un archivo.

Ese archivo se incrementa cada vez que incrementMonoCounter()se llama (cada carga de página). El archivo contiene el volcado de un búfer en binario y se almacena en un SSD.

No importa lo que haga, aparece un error o undefineden la consola.

Jeremy Dicaire
fuente
¿Responde esto a tu pregunta? Usando el sistema de archivos en node.js con async /
await

Respuestas:

165

Para usar await/ asyncnecesita métodos que devuelvan promesas. Las funciones principales de la API no hacen eso sin envoltorios 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 nota, readFileSyncno acepta una devolución de llamada, devuelve los datos o lanza una excepción. No obtiene el valor que desea porque la función que proporciona se ignora y no está capturando el valor de retorno real.

tadman
fuente
3
Gracias, no sabía que necesitaba envolver la API central. Usted es maravilloso.
Jeremy Dicaire
4
La API central es anterior a la especificación moderna de Promise y la adopción de async/ await, por lo que es un paso necesario. La buena noticia es que, promisifypor lo general, hace que funcione sin problemas.
tadman
1
Esto maneja el lío de no poder aprovechar async-await con FS normalmente. ¡Gracias por esto! ¡Me salvaste una tonelada! No hay una respuesta que realmente aborde este tema como el suyo.
jacobhobson
3
También await es algo redundante, ya que se puede inferir. Solo si desea explícitamente esperar en el ejemplo, puede hacerlo const file = await readFile...; return file;.
bigkahunaburger
1
@shijin Hasta que la API del núcleo de Node cambie a promesas, lo cual es poco probable en este momento, entonces sí. Sin embargo, hay envoltorios de NPM que lo hacen por usted.
tadman
150

Dado que las promesas de Node v11.0.0 fs están disponibles de forma nativa sin promisify:

const fs = require('fs').promises;
async function loadMonoCounter() {
    const data = await fs.readFile("monolitic.txt", "binary");
    return new Buffer(data);
}
Joel
fuente
4
sin bibliotecas adicionales, limpio y simple: esta debería ser la respuesta preferible
Adam Bubela
2
A partir del 21 de octubre de 2019, v12 es una versión LTS activa
cbronson
16
import { promises as fs } from "fs";si desea utilizar la sintaxis de importación.
tr3online
21

Esta es la versión TypeScript de la respuesta de @ Joel. Se puede usar después del Nodo 11.0:

import { promises as fs } from 'fs';

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

Puede envolver fácilmente el comando readFile con una promesa como esta:

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

luego usa:

await readFile("path/to/file");
Shlomi Schwartz
fuente
¿No se usa await dentro de la función asincrónica?
VikasBhat
@VikasBhat Sí, la línea de espera anterior se usaría dentro de otra función asíncrona, ya que la especificación lo requiere.
whoshotdk
8

Puede usar fs.promisesdisponible de forma nativa desde 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
fuente
Si solo desea usar promesas, puede hacer algo comoconst fs = require('fs').promises
nathanfranke
1

Hay un fs.readFileSync( path, options )método que es sincrónico.

George Ogden
fuente