Usando el sistema de archivos en node.js con async / await

129

Me gustaría usar async / await con algunas operaciones del sistema de archivos. Normalmente async / await funciona bien porque lo uso babel-plugin-syntax-async-functions.

Pero con este código me encuentro con el caso if donde namesno está definido:

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();

Cuando reconstruyo el código en la versión de devolución de llamada todo está bien y obtengo los nombres de archivo. Gracias por tus sugerencias.

Quellenangeber
fuente

Respuestas:

139

Comenzando con el nodo 8.0.0, puede usar esto:

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();

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

Azbykov
fuente
77
En el nodo v8.9.4, recibí un SyntaxError: Unexpected token importmensaje de error. ¿node8 admite importtoken por defecto?
makerj
9
@makerj está usando la nueva importsintaxis. Actualmente requiere algo de transpiling. Estaría bien usar también const fs = require('fs')oconst { promisify } = require('util')
Josh Sandlin
2
Pregunta novata, pero ¿cómo se {err, names} = functionllama la sintaxis?
Qasim
66
@Qasim se llama asignación de desestructuración.
jaredkwright
1
@AlexanderZeitler Eso puede ser cierto. No he buscado ver si ese es realmente un uso correcto de la desestructuración. En el caso de espera asíncrona, creo que lo haría names = await readdir('path/to/dir');y si hay un errcontrolador en el catchbloque. De cualquier manera, el nombre de la sintaxis es una tarea de desestructuración que fue solo en respuesta a la pregunta de Qasim.
jaredkwright
88

El soporte nativo para asíncrono espera funciones fs desde el Nodo 11

Desde Node.JS 11.0.0 (estable) y la versión 10.0.0 (experimental), tiene acceso a los métodos del sistema de archivos que ya están prometidos y puede usarlos con try catchmanejo de excepciones en lugar de verificar si el valor devuelto de la devolución de llamada contiene un error.

¡La API es muy limpia y elegante! Simplemente use.promises miembro del 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();
bman
fuente
Esta API es estable a partir de la versión 11.x según la documentación del sistema de archivos en el sitio Node.js
TheHanna
1
@ DanStarns si no lo return awaitprometes, el bloqueo no sirve de nada ... Creo que a veces es una buena práctica esperar antes de regresar
538ROMEO
@ 538ROMEO acaba de ver esto y tu derecho. Gracias por mencionarlo.
DanStarns
Documentación para estos métodos alternativos: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan Takhar
86

Node.js 8.0.0

Asíncrono nativo / espera

Promisificar

Desde esta versión, puede usar la función nativa Node.js de la 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()

Envoltura de promesas

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()

Consejo

Utilice siempre try..catchpara bloques de espera, si no desea volver a lanzar la excepción superior.

dimpiax
fuente
Esto es extraño. Recibo SyntaxError: waitit solo es válido en la función asíncrona ... llorando de rabia.
Vedran Maricevic.
2
@VedranMaricevic. mira los comentarios, awaitdebe estar siempre en asyncbloque :)
dimpiax
@VedranMaricevic. Necesitas llamar a eso const res = await readFile('data.json') console.log(res)en alguna función asíncrona
Jayraj
Prometer envolverlo fs.promisesy usarlo async/awaites muy confuso para mí
oldboy
@PrimitiveNom Promise se puede usar de manera tradicional dentro then, catchetc. Donde están asíncronos / aguardan es el flujo de comportamiento moderno.
dimpiax
43

Puede producir un comportamiento incorrecto porque File-Api fs.readdirno devuelve una promesa. Solo toma una devolución de llamada. Si desea utilizar la sintaxis de espera asíncrona, puede 'prometer' la función de esta manera:

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

y llámalo en su lugar:

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

A partir de v10.0 , puede usarfs.Promises

Ejemplo 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();

Ejemplo 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);
})();
DanStarns
fuente
Funciona muy bien, pero es importante tener en cuenta el problema abierto con respecto a la ExperimentalWarning: The fs.promises API is experimentaladvertencia: github.com/pnpm/pnpm/issues/1178
DavidP
1
@DavidP, ¿qué versión de nodo estás usando? 12 y superior funciona bien
DanStarns
2
¡Si! Absolutamente correcto - Olvidé indicar la versión en la que estoy: v10.15.3- es posible suprimir el mensaje. Sin embargo, con el problema aún abierto, pensé que valía la pena mencionarlo.
DavidP
1
@DavidP Quiero decir que vale la pena mencionar que no me malinterpreten, pero el nodo 12 está en LTS ahora, así que no es un Biggie.
DanStarns
¿cómo exactamente usas esto con, digamos readFile,? Soy nuevo en todo esto promete, y todo lo que quiero hacer es tener una función getContentque pueda llamar y esperar en varias partes a lo largo de mi guión, sin embargo, esto está resultando muy confuso
oldboy
8

Esta es la versión de TypeScript a la pregunta. Es utilizable 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
5

Esto es lo que funcionó para mí:

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);
  }
})();

Este código funciona en el nodo 7.6 sin babel cuando la bandera de la armonía está activada: node --harmony my-script.js. Y comenzando con el nodo 7.7, ¡ni siquiera necesita esta bandera !

La fspbiblioteca incluida al principio es solo un contenedor prometido para fs(y fs-ext).

¡Estoy realmente entusiasmado con lo que puedes hacer en el nodo sin babel en estos días! ¡Native async/ awaithacer que escribir código sea un placer!

ACTUALIZACIÓN 2017-06: el módulo fs-promise fue obsoleto. Utilice en su fs-extralugar con la misma API.

Alexander Kachkaev
fuente
Descargar una biblioteca para esto es una exageración, la hinchazón de dependencias es algo contra lo que la comunidad debería estar fuertemente en contra, de hecho, debería crearse un nuevo npmjs que solo tenga libs con 0 dependencias
PirateApp
5

Se recomienda utilizar un paquete npm como https://github.com/davetemplin/async-file , en comparación con las funciones personalizadas. Por ejemplo:

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');

Otras respuestas están desactualizadas

sean2078
fuente
5

Tengo este pequeño módulo de ayuda que exporta versiones de funciones prometidasfs

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

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