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.

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


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

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.


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

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

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

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.


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


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...
};

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.