การใช้ระบบไฟล์ใน node.js ด้วย async / await


130

ฉันต้องการใช้ async / await กับการทำงานของระบบไฟล์บางอย่าง โดยปกติ async / await ทำงานได้ดีเพราะฉันใช้babel-plugin-syntax-async-functionsไฟล์.

แต่ด้วยรหัสนี้ฉันพบใน if case ที่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();

เมื่อฉันสร้างรหัสใหม่ในเวอร์ชันนรกเรียกกลับทุกอย่างก็โอเคและฉันได้รับชื่อไฟล์ ขอบคุณสำหรับคำแนะนำ

คำตอบ:


140

เริ่มต้นด้วยโหนด 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โทเค็นโดยค่าเริ่มต้นหรือไม่
ชง

9
@makerj เขาใช้importไวยากรณ์ใหม่ ขณะนี้ต้องมีการถ่ายเท จะใช้ได้ด้วยconst fs = require('fs')หรือconst { promisify } = require('util')
Josh Sandlin

2
คำถาม Noob แต่{err, names} = functionไวยากรณ์เรียกว่าอะไร?
Qasim

6
@ Qasim เรียกว่าการมอบหมายการทำลายโครงสร้าง
jaredkwright

1
@AlexanderZeitler นั่นอาจจะเป็นเรื่องจริง ฉันไม่ได้ดูว่านั่นเป็นการใช้การทำลายล้างที่ถูกต้องหรือไม่ ในกรณีของ async รอฉันคิดว่าคุณจะทำnames = await readdir('path/to/dir');และถ้ามีที่errจับในcatchบล็อก ไม่ว่าจะด้วยวิธีใดชื่อของไวยากรณ์คือการทำลายการมอบหมายงานซึ่งเป็นเพียงการตอบสนองต่อคำถามของ Qasim
jaredkwright

88

การสนับสนุนดั้งเดิมสำหรับ async รอฟังก์ชัน fs ตั้งแต่โหนด 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 Takhar

87

Node.js 8.0.0

async ดั้งเดิม / รอ

Promisify

จากเวอร์ชันนี้คุณสามารถใช้ฟังก์ชัน Node.js ดั้งเดิมจากไลบรารี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()


การห่อสัญญา

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: await ใช้ได้ในฟังก์ชัน async เท่านั้น ... ร้องไห้ด้วยความโกรธ
Vedran Maricevic

2
@VedranMaricevic ดูความคิดเห็นawaitต้องอยู่ในasyncบล็อกเสมอ:)
dimpiax

@VedranMaricevic คุณต้องเรียกสิ่งนั้นconst res = await readFile('data.json') console.log(res)ในฟังก์ชัน async
Jayraj

สัญญาว่าจะห่อfs.promisesและใช้ด้วยasync/awaitทำให้ฉันสับสน
oldboy

@PrimitiveNom สัญญาสามารถนำมาใช้ในวิธีแบบดั้งเดิมภายในthen, catchฯลฯ ที่ไหน async / รอคอยคือการไหลของพฤติกรรมที่ทันสมัย
dimpiax

43

คุณอาจสร้างพฤติกรรมที่ไม่ถูกต้องเนื่องจาก File-Api fs.readdirไม่ส่งคืนสัญญา ใช้เวลาเพียงโทรกลับ หากคุณต้องการใช้ไวยากรณ์ async-await คุณสามารถ 'promisify' ฟังก์ชันนี้ได้:

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 โดยไม่ต้อง Babel เมื่อธงสามัคคีnode --harmony my-script.jsถูกเปิดใช้งาน: และเริ่มต้นด้วยโหนด 7.7 คุณไม่จำเป็นต้องใช้แฟล็กนี้ด้วยซ้ำ !

fspห้องสมุดรวมอยู่ในจุดเริ่มต้นเป็นเพียงเสื้อคลุม promisified สำหรับfs(และfs-ext)

ฉันออกไปแล้วจริงๆเกี่ยวกับสิ่งที่คุณสามารถทำได้ในโหนดโดยไม่มี Babel ในทุกวันนี้! เนทีฟasync/ awaitเขียนโค้ดให้เป็นความสุข!

อัปเดต 2017-06:เลิกใช้โมดูล fs- promแล้ว ใช้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

ฉันมีเล็ก ๆ นี้ช่วยให้โมดูลที่ส่งออกpromisifiedรุ่นของfsฟังก์ชั่น

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

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.