Sử dụng hệ thống tệp trong node.js với async / await


129

Tôi muốn sử dụng async / await với một số hoạt động hệ thống tệp. Thông thường async / await hoạt động tốt vì tôi sử dụng babel-plugin-syntax-async-functions.

Nhưng với mã này, tôi gặp trường hợp nếu nameskhông được xác định:

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

Khi tôi xây dựng lại mã thành phiên bản địa ngục gọi lại, mọi thứ đều ổn và tôi nhận được tên tệp. Cảm ơn những gợi ý của bạn.

Câu trả lời:


139

Bắt đầu với nút 8.0.0, bạn có thể sử dụng cái này:

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

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


7
Trong nút v8.9.4, có một SyntaxError: Unexpected token importthông báo lỗi. node8 có hỗ trợ importmã thông báo theo mặc định không?
makerj

9
@makerj anh ấy đang sử dụng importcú pháp mới . Nó hiện yêu cầu một số chuyển ngữ. Sẽ là ok để còn sử dụng const fs = require('fs')hoặcconst { promisify } = require('util')
Josh Sandlin

2
Câu hỏi Noob, nhưng {err, names} = functioncú pháp được gọi là gì?
Qasim

6
@Qasim nó được gọi là phân công hủy cấu trúc.
jaredkwright

1
@AlexanderZeitler Điều đó có thể đúng. Tôi chưa xem xét liệu đó có thực sự là cách sử dụng chính xác của cấu trúc hay không. Trong trường hợp không đồng bộ đang chờ đợi, tôi nghĩ bạn sẽ làm names = await readdir('path/to/dir');và nếu có một errxử lý nó trong catchkhối. Dù bằng cách nào, tên của cú pháp là phép gán hủy cấu trúc, đáp ứng cho câu hỏi của Qasim.
jaredkwright

88

Hỗ trợ gốc cho các hàm fs không đồng bộ đang chờ đợi kể từ Node 11

Kể từ Node.JS 11.0.0 (ổn định) và phiên bản 10.0.0 (thử nghiệm), bạn có quyền truy cập vào các phương thức hệ thống tệp đã được quảng cáo và bạn có thể sử dụng chúng với try catchxử lý ngoại lệ thay vì kiểm tra xem giá trị trả về của lệnh gọi lại có chứa một lỗi.

API rất sạch sẽ và trang nhã! Chỉ cần sử dụng .promisesthành viên của fsđối tượng:

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 này là ổn định như các phiên bản 11.x theo File System tài liệu trên trang web Node.js
TheHanna

1
@DanStarns nếu bạn không return awaithứa, khối bắt sẽ không có ích gì ... Tôi mỏngj đôi khi là một phương pháp hay để chờ trước khi quay lại
538ROMEO

@ 538ROMEO vừa xem xét điều này và quyền của bạn. Cảm ơn đã chỉ ra điều đó.
DanStarns

Tài liệu cho các phương pháp thay thế này: nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan Takhar

87

Node.js 8.0.0

Native async / await

Hứa hẹn

Từ phiên bản này, bạn có thể sử dụng chức năng Node.js có nguồn gốc từ util thư viện.

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

Gói lời hứa

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

Khuyên bảo

Luôn sử dụng try..catchcho các khối chờ đợi, nếu bạn không muốn ném lại ngoại lệ phía trên.


Điều này thật kỳ lạ. Tôi đang nhận được SyntaxError: await chỉ hợp lệ trong hàm không đồng bộ ... đang khóc trong cơn thịnh nộ.
Vedran Maricevic.

2
@VedranMaricevic. nhìn vào ý kiến, awaitphải luôn trong asynckhối :)
dimpiax

@VedranMaricevic. Bạn cần gọi điều đó const res = await readFile('data.json') console.log(res)trong một số hàm không đồng bộ
Jayraj 19/02/19

Gói lời hứa fs.promisesvà sử dụng nó với tôi async/awaitthật là khó hiểu
oldboy

@PrimitiveNom Promise có thể được sử dụng theo cách truyền thống bên trong then, catchv.v. Ở đâu async / await là luồng hành vi hiện đại.
dimpiax

43

Bạn có thể tạo ra hành vi sai vì File-Api fs.readdirkhông trả lại lời hứa. Nó chỉ mất một cuộc gọi lại. Nếu bạn muốn sử dụng cú pháp async-await, bạn có thể 'quảng bá' hàm như sau:

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

và gọi nó là:

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

31

Kể từ v10.0 , bạn có thể sử dụngfs.Promises

Ví dụ sử dụng 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();

Ví dụ sử dụng 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);
})();

Các công trình lớn, nhưng quan trọng cần lưu ý các vấn đề mở liên quan đến việc ExperimentalWarning: The fs.promises API is experimentalcảnh báo: github.com/pnpm/pnpm/issues/1178
DavidP

1
@DavidP bạn đang sử dụng phiên bản nút nào? 12 và các công trình trên tinh
DanStarns

2
Đúng! Hoàn toàn chính xác - tôi đã bỏ qua phiên bản tôi đang sử dụng: v10.15.3- có thể chặn thông báo. Tuy nhiên, với vấn đề vẫn còn bỏ ngỏ, tôi nghĩ nó đáng nói.
DavidP

1
@DavidP Ý tôi là điều đáng nói là đừng hiểu nhầm tôi, nhưng nút 12 hiện đang ở trong LTS nên nó không phải là Biggie.
DanStarns

làm thế nào để bạn sử dụng chính xác cái này với, nói readFile,? im mới để lời hứa toàn bộ điều này, và tất cả tôi muốn làm là có một chức năng getContentmà tôi có thể gọi và chờ đợi ở các bộ phận khác nhau trong suốt kịch bản của tôi, tuy nhiên điều này được chứng minh rất khó hiểu
Oldboy

8

Đây là phiên bản TypeScript cho câu hỏi. Nó có thể sử dụng được sau 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

Đây là những gì đã làm việc cho tôi:

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

Mã này hoạt động trong nút 7,6 mà không babel khi hòa cờ được kích hoạt: node --harmony my-script.js. Và bắt đầu với nút 7.7, bạn thậm chí không cần cờ này !

Các fspthư viện có trong đầu chỉ là một wrapper promisified cho fs(và fs-ext).

Tôi thực sự thích thú về những gì bạn có thể làm trong node mà không có babel những ngày này! Bản địa async/ awaitlàm cho việc viết mã trở thành một niềm vui như vậy!

CẬP NHẬT 2017-06: mô-đun fs-promise không được dùng nữa. Sử dụng fs-extrathay thế với cùng một API.


Tải một thư viện cho điều này là quá mức cần thiết tinh khiết, phụ thuộc đầy hơi là điều mà cộng đồng nên mạnh mẽ chống lại, Infact một npmjs mới nên đi vào làm mà chỉ có libs với 0 phụ thuộc
PirateApp

5

Khuyến nghị sử dụng gói npm chẳng hạn như https://github.com/davetemplin/async-file , so với các hàm tùy chỉnh. Ví dụ:

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

Các câu trả lời khác đã lỗi thời


5

Tôi có mô-đun trợ giúp nhỏ này xuất các phiên bản chức năng được quảng báfs

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

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.