Comment lire correctement un fichier avec async / wait?

121

Je ne peux pas comprendre comment async/ awaitfonctionne. Je le comprends un peu mais je ne peux pas le faire fonctionner.

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

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

Je sais que je pourrais utiliser readFileSync, mais si je le fais, je sais que je ne comprendrai jamais async/ awaitet je vais simplement enterrer le problème.

Objectif: appeler loadMonoCounter()et renvoyer le contenu d'un fichier.

Ce fichier est incrémenté à chaque incrementMonoCounter()appel (à chaque chargement de page). Le fichier contient le vidage d'un tampon en binaire et est stocké sur un SSD.

Peu importe ce que je fais, j'obtiens une erreur ou undefineddans la console.

Jérémy Dicaire
la source
Est-ce que cela répond à votre question? Utilisation du système de fichiers dans node.js avec async /
await

Réponses:

166

Pour utiliser await/ asyncvous avez besoin de méthodes qui renvoient des promesses. Les fonctions principales de l'API ne font pas cela sans des wrappers comme 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);
})

À noter, readFileSyncne prend pas de rappel, il renvoie les données ou lève une exception. Vous n'obtenez pas la valeur souhaitée car la fonction que vous fournissez est ignorée et vous ne capturez pas la valeur de retour réelle.

tadman
la source
3
Merci, je ne savais pas que j'avais besoin d'encapsuler l'API principale. Tu es incroyable.
Jeremy Dicaire
4
L'API de base est antérieure à la spécification Promise moderne et à l'adoption de async/ await, c'est donc une étape nécessaire. La bonne nouvelle est promisifyque cela fonctionne généralement sans gâchis.
tadman
1
Cela gère le désordre de ne pas pouvoir exploiter normalement async-await avec FS. Merci pour ça! Vous m'avez sauvé une tonne! Il n'y a pas de réponse qui aborde vraiment cela comme la vôtre.
jacobhobson
3
Attendre est également un peu redondant car il peut être déduit. Vous ne pouvez le faire que si vous voulez explicitement attendre dans l'exemple const file = await readFile...; return file;.
bigkahunaburger
1
@shijin Jusqu'à ce que l'API principale de Node passe aux promesses, ce qui est peu probable à ce stade, alors oui. Il existe cependant des wrappers NPM qui le font pour vous.
tadman
152

Depuis Node v11.0.0, les promesses fs sont disponibles nativement sans promisify:

const fs = require('fs').promises;
async function loadMonoCounter() {
    const data = await fs.readFile("monolitic.txt", "binary");
    return new Buffer(data);
}
Joël
la source
4
pas de bibliothèques supplémentaires, propres et simples - cela devrait être une réponse préférable
Adam Bubela
2
À partir du 21 octobre 2019, la v12 est une version LTS active
cbronson
16
import { promises as fs } from "fs";si vous souhaitez utiliser la syntaxe d'importation.
tr3online
21

C'est la version TypeScript de la réponse de @ Joel. Il est utilisable après le Node 11.0:

import { promises as fs } from 'fs';

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

Vous pouvez facilement encapsuler la commande readFile avec une promesse comme ceci:

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

puis utilisez:

await readFile("path/to/file");
Shlomi Schwartz
la source
Wait n'est-il pas utilisé dans la fonction asynchrone?
VikasBhat le
@VikasBhat Oui, la ligne d'attente ci-dessus serait utilisée dans une autre fonction asynchrone comme la spécification l'exige.
whoshotdk
8

Vous pouvez utiliser fs.promisesdisponible nativement depuis 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
la source
Si vous ne voulez utiliser que des promesses, vous pouvez faire quelque chose commeconst fs = require('fs').promises
nathanfranke
1

Il existe une fs.readFileSync( path, options )méthode synchrone.

George Ogden
la source