Використання файлової системи в node.js з функцією async / wait


129

Я хотів би використовувати async / wait з деякими операціями з файловою системою. Як правило, асинхронізація / очікування працює добре, тому що я використовую babel-plugin-syntax-async-functions.

Але з цим кодом я стикаюся з випадком if, де namesне визначено:

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

Коли я перестроюю код у версію пекла зворотного виклику, все гаразд, і я отримую імена файлів. Дякуємо за підказки

Відповіді:


139

Починаючи з вузла 8.0.0, ви можете використовувати це:

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

Дивіться https://nodejs.org/dist/latest-v8.x/docs/api/util.html#util_util_promisify_original


7
У вузлі v8.9.4 надійшло SyntaxError: Unexpected token importповідомлення про помилку. чи node8 підтримує importмаркер за замовчуванням?
makerj

9
@makerj він використовує новий importсинтаксис. Наразі це вимагає певної трансляції. Було б нормально також використовувати const fs = require('fs')абоconst { promisify } = require('util')
Джош Сандлін

2
Питання Noob, але як {err, names} = functionназивається синтаксис?
Касим

6
@Qasim це називається завданням деструктуризації.
jaredkwright

1
@AlexanderZeitler Це може бути правдою. Я не дивився, чи є це насправді правильним використанням руйнування. У випадку асинхронізації чекаю, я думаю, ви б просто зробили це, names = await readdir('path/to/dir');і якщо errв catchблоці є обробка . Так чи інакше, назва синтаксису - це руйнування призначення, яке було якраз у відповідь на питання Касима.
jaredkwright

88

Рідна підтримка асинхронізації очікує функцій fs з Node 11

Оскільки Node.JS 11.0.0 (стабільний) та версія 10.0.0 (експериментальна), у вас є доступ до методів файлової системи, які вже промальовані, і ви можете використовувати їх з try catchобробкою виключень, а не перевіряти, чи містить повернене значення зворотного виклику помилка.

API дуже чистий та елегантний! Просто використовуйте .promisesчлен fsоб'єкта:

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

Цей API стабільний, починаючи з версії 11.x, у документації щодо файлової системи на сайті Node.js
TheHanna

1
@DanStarns, якщо ви не return awaitобіцяєте, блок лову не принесе користі ... Я думаю, що іноді корисно чекати перед поверненням
538ROMEO

@ 538ROMEO щойно вивчив це і ваше право. Дякуємо, що вказали на це.
DanStarns

Документація для цих альтернативних методів: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan

87

Node.js 8.0.0

Рідна асинхроніка / очікування

Обіцяти

З цієї версії ви можете використовувати нативну функцію Node.js з бібліотеки утилів .

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

Пообіцяйте обгортання

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

Поради

Завжди використовуйте try..catchдля блоків, що очікують, якщо ви не хочете перезавантажувати виняток у верхньому.


Це дивно. Я отримую SyntaxError: очікування дійсне лише в функції асинхронізації ... плаче в люті.
Ведран Марічевич.

2
@VedranMaricevic. подивіться на коментарі, awaitповинні бути завжди в asyncблоці :)
dimpiax

@VedranMaricevic. Вам потрібно зателефонувати const res = await readFile('data.json') console.log(res)в якусь функцію асинхронізації
Jayraj

обіцяю обгортання fs.promisesта використання його async/awaitнастільки бентежить мене
oldboy

@PrimitiveNom Promise може бути використаний традиційним способом в межах then, і catchт.д. Де асинхронне / Await сучасного потоку поведінки.
dimpiax

43

Ви можете викликати неправильну поведінку, оскільки File-Api fs.readdirне повертає обіцянки. Він займає лише зворотний дзвінок. Якщо ви хочете перейти з синтаксисом async-await, ви можете «обізначити» функцію так:

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

і зателефонуйте замість цього:

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

31

Станом на v10.0 ви можете використовуватиfs.Promises

Приклад використання 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();

Приклад використання 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);
})();

Працює відмінно, але важливо відзначити , відкрите питання щодо ExperimentalWarning: The fs.promises API is experimentalпопередження: github.com/pnpm/pnpm/issues/1178
DavidP

1
@DavidP, яку версію вузла ви використовуєте? 12 і вище працює добре
DanStarns

2
Так! Абсолютно правильно - я знехтував версією стану, на якій я перебуваю: v10.15.3- повідомлення можна придушити. Однак, коли питання все ще залишається відкритим, я вважав, що це варто згадати.
DavidP

1
@DavidP Я маю на увазі, що варто згадати, не зрозумійте мене неправильно, але вузол 12 зараз у LTS, тому це не Biggie.
DanStarns

як саме ви цим користуєтесь, скажімо readFile,? Я новачок у цій цілій обіцянці, і все, що я хочу зробити, - це функція, getContentяку я можу дзвонити і чекати в різних частинах у всьому моєму сценарії, але це доводить дуже заплутано
oldboy

8

Це версія TypeScript до питання. Він може бути використаний після Вузла 11.0:

import { promises as fs } from 'fs';

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

5

Ось що для мене спрацювало:

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

Цей код працює в вузлі 7.6 без Бабеля , коли прапор гармонії включений: node --harmony my-script.js. А починаючи з вузла 7.7, вам навіть не потрібен цей прапор !

fspБібліотека включена на початку це просто promisified обгортка для fsfs-ext).

Мені дуже цікаво, що ти можеш зробити у вузлі без джебла сьогодні! Рідні async/ awaitзробіть написання коду таким задоволенням!

ОНОВЛЕННЯ 2017-06: модуль fs- promis був застарілим. Використовуйте fs-extraзамість того ж API.


Завантаження бібліотеки для цього є чистою надмірністю, здуття залежностей - це те, проти чого громада повинна бути рішуче проти, внаслідок інфактності повинен з’явитися новий npmjs, який має лише libs з 0 залежностями
PirateApp

5

Рекомендуйте використовувати пакет npm, такий як https://github.com/davetemplin/async-file , порівняно зі спеціальними функціями. Наприклад:

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

Інші відповіді застаріли


5

У мене є цей маленький допоміжний модуль, який експортує багатообіцяні версії fsфункцій

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

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

1

Вузол v14.0.0 і вище

ви можете просто зробити:

import { readdir } from "fs/promises";

так само, як ви імпортували б з "fs"

див. цей PR для отримання більш детальної інформації: https://github.com/nodejs/node/pull/31553

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.