Как правильно читать файл с помощью async / await?

121

Я не могу понять, как async/ awaitработает. Я немного понимаю, но не могу заставить работать.

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

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

Я знаю, что могу использовать readFileSync, но если я это сделаю, я знаю, что никогда не пойму async/ awaitи я просто похороню проблему.

Цель: вызвать loadMonoCounter()и вернуть содержимое файла.

Этот файл увеличивается каждый раз при incrementMonoCounter()вызове (при каждой загрузке страницы). Файл содержит дамп буфера в двоичном формате и хранится на SSD.

Что бы я ни делал, у меня вылетает ошибка или undefinedв консоли.

Джереми Дикэр
источник

Ответы:

168

Для использования await/ asyncвам нужны методы, возвращающие обещания. Основные функции API не делают этого без таких оболочек, как 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);
})

В качестве примечания, readFileSyncон не принимает обратный вызов, он возвращает данные или выдает исключение. Вы не получаете желаемое значение, потому что эта функция, которую вы предоставляете, игнорируется, и вы не фиксируете фактическое возвращаемое значение.

тадман
источник
3
Спасибо, я не знал, что мне нужно обернуть основной API. Ты обалденный.
Джереми Дикэр
4
Базовый API предшествует современной спецификации Promise и принятию async/ await, так что это необходимый шаг. Хорошая новость в том, promisifyчто она обычно работает без проблем.
tadman
1
Это устраняет беспорядок, связанный с невозможностью нормально использовать async-await с FS. Спасибо тебе за это! Ты спас мне тонну! Нет ответа, который бы подходил к этому, как ваш.
jacobhobson
3
Также await является своего рода избыточным, поскольку его можно вводить. Вы можете сделать это только в том случае, если вы явно хотите использовать await в примере const file = await readFile...; return file;.
bigkahunaburger
1
@shijin Пока API ядра Node не переключится на обещания, что маловероятно на данном этапе, тогда да. Однако есть оболочки NPM, которые делают это за вас.
tadman
153

Поскольку Node v11.0.0 fs promises изначально доступен без promisify:

const fs = require('fs').promises;
async function loadMonoCounter() {
    const data = await fs.readFile("monolitic.txt", "binary");
    return new Buffer(data);
}
Джоэл
источник
4
никаких дополнительных библиотек, чистый и простой - это должен быть предпочтительный ответ
Адам Бубела 09
2
По состоянию на 21 октября 2019 года v12 является активной LTS-версией
cbronson
16
import { promises as fs } from "fs";если вы хотите использовать синтаксис импорта.
tr3online
21

Это TypeScript-версия ответа @Joel. Его можно использовать после 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
источник
18

Вы можете легко обернуть команду readFile таким обещанием:

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

затем используйте:

await readFile("path/to/file");
Шломи Шварц
источник
Разве ожидание не используется внутри асинхронной функции?
VikasBhat
@VikasBhat Да, указанная выше строка ожидания будет использоваться внутри другой асинхронной функции, поскольку этого требует спецификация.
whoshotdk
8

Вы можете использовать fs.promisesдоступный изначально, начиная с 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
источник
Если вы хотите использовать только обещания, вы можете сделать что-нибудь вродеconst fs = require('fs').promises
натанфранке
1

Есть fs.readFileSync( path, options )метод, который синхронный.

Джордж Огден
источник