async / awaitでnode.jsのファイルシステムを使用する


129

一部のファイルシステム操作でasync / awaitを使用したいと思います。通常はasync / awaitを使用しているため、正常に動作しますbabel-plugin-syntax-async-functions

しかし、このコードでnamesは、ifケースが未定義の場合に遭遇します。

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 2018年

9
@makerj彼は新しいimport構文を使用しています。現在、いくつかのトランスパイルが必要です。const fs = require('fs')or も使用できますconst { promisify } = require('util')
Josh Sandlin

2
Noobの質問ですが、{err, names} = function構文は何と呼ばれますか?
カシム2018年

6
@カシムそれは破壊的な割り当てと呼ばれています。
jaredkwright

1
@AlexanderZeitlerそれは本当かもしれません。それが実際に解体の正しい使い方であるかどうか、私は見たことがありません。非同期待ちの場合、私はあなたがそうするだろうと思いますnames = await readdir('path/to/dir');、そしてerrハンドルがcatchブロックにあれば。いずれにせよ、構文の名前は、カシムの質問に答えただけの破壊的な割り当てです。
jaredkwright、

88

Node 11以降の非同期afs fs関数のネイティブサポート

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は、Node.jsサイトのファイルシステムのドキュメント
TheHanna

1
@DanStarns return await約束を果たさない場合、catchブロックは役に立たない...戻る前に待つことをお勧めします
538ROMEO

@ 538ROMEOはこれとあなたの権利を調べました。指摘してくれてありがとう。
DanStarns

これらの代替メソッドのドキュメント:nodejs.org/api/fs.html#fs_fs_promises_api
Jeevan

86

Node.js 8.0.0

ネイティブ非同期/待機

約束する

このバージョンから、utilライブラリのネイティブ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が表示されます:awaitは非同期関数でのみ有効です...
Vedran Maricevic。

2
@VedranMaricevic。コメントを見て、await常にasyncブロック内にある必要があります:)
dimpiax '20

@VedranMaricevic。const res = await readFile('data.json') console.log(res)一部の非同期関数でそれを呼び出す必要があります
Jayraj

ラップfs.promisesすることを約束し、それを使用することasync/awaitは私にとってとても混乱しています
oldboy '28

@PrimitiveNom約束は内の伝統的な方法で使用することができthencatch非同期/のawaitは、近代的な動作の流れであるされている場合など。
dimpiax

43

File-Api fs.readdirはpromiseを返さないため、間違った動作をする可能性があります。コールバックのみを受け取ります。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バージョンです。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

これが私のために働いたものです:

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

私は最近、バベルなしでノードで何ができるかについて本当にうんざりしています!ネイティブasync/ awaitコードを書くことをそのような喜びにする!

UPDATE 2017-06: fs-promiseモジュールは非推奨になりました。fs-extra代わりに同じAPIを使用してください。


このためのライブラリのダウンロードはやり過ぎです。依存関係の肥大化はコミュニティが強く反対すべきものです。つまり、依存関係が0のライブラリのみを持つ新しいnpmjsが作成されるはずです
PirateApp

5

カスタム関数と比較して、https://github.com/davetemplin/async-fileなどのnpmパッケージを使用することをお勧めします。例えば:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.