Menggunakan filesystem di node.js dengan async / await

129

Saya ingin menggunakan async / await dengan beberapa operasi sistem file. Biasanya async / await berfungsi dengan baik karena saya menggunakan babel-plugin-syntax-async-functions.

Tetapi dengan kode ini saya mengalami kasus if di mana namestidak ditentukan:

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

Ketika saya membangun kembali kode ke dalam versi neraka panggilan balik semuanya OK dan saya mendapatkan nama file. Terima kasih atas petunjuk Anda.

Quellenangeber
sumber

Jawaban:

139

Dimulai dengan node 8.0.0, Anda dapat menggunakan ini:

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

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

Azbykov
sumber
7
Pada node v8.9.4, mendapat SyntaxError: Unexpected token importpesan error. apakah node8 mendukung importtoken secara default?
makerj
9
@makerj dia menggunakan importsintaks baru . Saat ini membutuhkan transpiling. Akan baik-baik saja untuk menggunakan const fs = require('fs')atauconst { promisify } = require('util')
Josh Sandlin
2
Pertanyaan noob, tapi apa nama {err, names} = functionsintaksnya?
Qasim
6
@Qasim itu disebut tugas destrukturisasi.
jaredkwright
1
@Alexanderee Itu mungkin benar. Saya belum melihat apakah itu benar-benar penggunaan penghancuran yang benar. Dalam kasus async menunggu saya pikir Anda hanya akan melakukannya names = await readdir('path/to/dir');dan jika ada errmenanganinya di catchblok. Either way, nama sintaksis tugas merusak yang hanya menanggapi pertanyaan Qasim.
jaredkwright
88

Dukungan asli untuk async menunggu fungsi fs sejak Node 11

Sejak Node.JS 11.0.0 (stabil), dan versi 10.0.0 (eksperimental), Anda memiliki akses ke metode sistem file yang sudah dijanjikan dan Anda dapat menggunakannya dengan try catchpenanganan pengecualian daripada memeriksa apakah nilai yang dikembalikan callback berisi sebuah kesalahan.

API-nya sangat bersih dan elegan! Cukup gunakan .promisesanggota fsobjek:

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
sumber
API ini stabil pada versi 11.x sesuai dokumentasi Sistem File di situs Node.js
TheHanna
1
@DanStarns jika Anda tidak return awaitberjanji, blok tangkapan tidak ada gunanya ... Saya pikir kadang-kadang merupakan praktik yang baik untuk menunggu sebelum kembali
538ROMEO
@ 538ROMEO baru saja melihat ini & hak Anda. Terima kasih telah menunjukkannya.
DanStarns
Dokumentasi untuk metode alternatif ini: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan Takhar
87

Node.js 8.0.0

Asinkron asli / menunggu

Promisify

Dari versi ini, Anda dapat menggunakan fungsi Node.js asli dari pustaka 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()

Pembungkus Janji

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

Nasihat

Selalu gunakan try..catchuntuk blok menunggu, jika Anda tidak ingin menampilkan kembali pengecualian ke atas.

dimpiax
sumber
Ini aneh. Saya mendapatkan SyntaxError: await hanya valid dalam fungsi async ... menangis dalam amarah.
Vedran Maricevic.
2
@Tokopedia. lihat komentar, awaitharus selalu di asyncblok :)
dimpiax
@Tokopedia. Anda perlu memanggilnya const res = await readFile('data.json') console.log(res)di beberapa fungsi asinkron
Jayraj
janji membungkus fs.promisesdan menggunakannya dengan async/awaitsangat membingungkan saya
oldboy
@PrimitiveNom Promise dapat digunakan dengan cara tradisional di dalam then, catchdll. Di mana async / await adalah aliran perilaku modern.
dimpiax
43

Anda mungkin menghasilkan perilaku yang salah karena File-Api fs.readdirtidak mengembalikan janji. Ini hanya membutuhkan panggilan balik. Jika Anda ingin menggunakan sintaks async-await, Anda dapat 'menjanjikan' fungsi seperti ini:

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

dan sebut saja:

names = await readdirAsync('path/to/dir');
wursttheke.dll
sumber
31

Mulai v10.0 , Anda dapat menggunakanfs.Promises

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

Contoh penggunaan 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
sumber
Berfungsi dengan baik, tetapi penting untuk mencatat masalah terbuka terkait ExperimentalWarning: The fs.promises API is experimentalperingatan: github.com/pnpm/pnpm/issues/1178
DavidP
1
@DavidP versi node apa yang Anda gunakan?
Usia
2
Iya! Benar sekali - Saya lalai menyatakan versi saya: v10.15.3- itu mungkin untuk menyembunyikan pesan. Namun, dengan masalah yang masih terbuka, saya pikir itu layak untuk disebutkan.
DavidP
1
@DavidP Maksud saya itu layak disebutkan jangan salah paham, tetapi node 12 ada di LTS sekarang jadi ini bukan masalah besar.
DanStarns
bagaimana tepatnya Anda menggunakan ini dengan, katakanlah readFile,? Saya baru dalam hal keseluruhan janji ini, dan yang ingin saya lakukan hanyalah memiliki fungsi getContentyang dapat saya panggil dan tunggu di berbagai bagian di seluruh skrip saya, namun ini terbukti sangat membingungkan
oldboy
8

Ini adalah versi TypeScript untuk pertanyaan tersebut. Ini dapat digunakan setelah 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
sumber
5

Inilah yang berhasil untuk saya:

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

Kode ini bekerja di simpul 7,6 tanpa babel ketika harmoni bendera diaktifkan: node --harmony my-script.js. Dan dimulai dengan node 7.7, Anda bahkan tidak membutuhkan flag ini !

The fspperpustakaan termasuk dalam awal hanya bungkus promisified untuk fs(dan fs-ext).

Saya sangat bersemangat tentang apa yang dapat Anda lakukan di node tanpa babel hari ini! Native async/ awaitbuat kode penulisan sangat menyenangkan!

UPDATE 2017-06: modul fs-promise tidak digunakan lagi. Gunakan fs-extrasebagai gantinya dengan API yang sama.

Alexander Kachkaev
sumber
Mengunduh perpustakaan untuk ini benar-benar berlebihan, ketergantungan yang membengkak adalah sesuatu yang harus sangat dilawan oleh komunitas, bahkan npmj baru harus dibuat yang hanya memiliki libs dengan 0 dependensi
PirateApp
5

Rekomendasikan menggunakan paket npm seperti https://github.com/davetemplin/async-file , dibandingkan dengan fungsi kustom. Sebagai contoh:

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

Jawaban lain sudah ketinggalan zaman

sean2078
sumber
5

Saya memiliki modul bantuan kecil ini yang mengekspor versi fungsi yang dijanjikanfs

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

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

Node v14.0.0 dan yang lebih baru

Anda bisa melakukan:

import { readdir } from "fs/promises";

seperti saat Anda mengimpor dari "fs"

lihat PR ini untuk lebih jelasnya: https://github.com/nodejs/node/pull/31553

Konrad
sumber