LoginSignup
1
3

More than 3 years have passed since last update.

Node.js: FileSystem モジュールの非同期関数を async/await で使ってみた。

Posted at

(サンプル1) Promise を直接使う場合 (v10.x.x)

async/await の使い方のサンプルはたくさんあるが、Node.js のモジュール関数(非同期)で実際に使うサンプルがなかった(少ない)ので、試しに作ってみた。

なお、使った Node.js のバージョンは 10.15.2 である。このバージョンでは fsPromise は experimental であるので警告が出る。気になる場合は v12 以上を使う必要がある。

次のサンプルは、fs.mkdir(path[, options], callback) の使用例である。/home/user/temp というフォルダの下にsub1を作り、さらに sub1/sub2 を作り、最後に sub1/sub2/sub3 を作る。

sample1.js
/* ディレクトリ作成  c:/temp/sub1/sub2/sub3 を作成 */
'use strict';
const fs = require("fs");

const dir0 = "/home/user/temp/";
const dir1 = "sub1/";
const dir2 = "sub2/";
const dir3 = "sub3/";

/* 指定したディレクトリを非同期で作成する。*/
function make_dir(dir) {
    return new Promise(resolve=>{
        fs.mkdir(dir, err=>{
           resolve(err);
        });
    });
}

/* ディレクトリ作成をここで順番に実行する。return で値を返さないこと。*/
async function makedirs_exec() {
    let err;
    let dir = dir0 + dir1;
    err = await make_dir(dir);
    if (err != null) {
        console.log(err.message);
        return;
    }
    dir += dir2;
    err = await make_dir(dir);
    if (err != null) {
        console.log(err.message);
        return;
    }
    dir += dir3;
    err = await make_dir(dir);
    if (err != null) {
        console.log(err.message);
        return;
    }
    console.log("END");
}

/* サブディレクトリを順番に作成する。*/
makedirs_exec();

await は async 関数の中でしか使えない。 await できる関数は Promise オブジェクトを返す必要があり、その内部でペンディングが解消したとき、resolve() を呼び出す必要がある。

上の例では、make_dir(dir) がその関数である。

async 関数は makedirs_exec() であり、内部で await を使ってペンディングが解消するまで停止し、順に深い場所のサブディレクトリを作成している。

最後に、makedirs_exec() をコールして処理を実行している。

(サンプル2) fsPromise 関数を使う場合 (v12.x.x以上)

v10.x.x までは fsPromise 関数は正式にサポートされてないため、警告が出るが、v12.x.x では正式サポートされたようである。

ただし、大部分の関数は v10.x.x でも stable のようなのでv12.x.xでもコードを書き換えずにそのまま実行できると思われる。

次のサンプルは、fsPromise の rmdir(path) 関数を使って、前のサンプルで作ったディレクトリを削除する。

fsPromise モジュール関数では、関数自体が Promise オブジェクトを返すので、ラッパー関数 (サンプル1のmake_dir(dir)に相当) を作る必要がなく、実にシンプルになる。

sample2.js
/* fsPromise の rmdir 関数を使ってディレクトリを削除する例 */
const fs_p = require("fs").promises;

const dir1 = "/home/user/temp/sub1";
const dir2 = "/home/user/temp/sub1/sub2";
const dir3 = "/home/user/temp/sub1/sub2/sub3";

async function remove_dirs() {
    /* sub3 を削除 */
    let err = await fs_p.rmdir(dir3);
    if (err == null) {
        console.log(`${dir3} was deleted.`);
    }
    else {
        console.log(err.message);
        return;
    }

    /* sub2 を削除 */
    err = await fs_p.rmdir(dir2);
    if (err == null) {
        console.log(`${dir2} was deleted.`);
    }
    else {
        console.log(err.message);
        return;
    }

    /* sub1 を削除 */
    err = await fs_p.rmdir(dir1);
    if (err == null) {
        console.log(`${dir1} was deleted.`);
    }
    else {
        console.log(err.message);
        return;
    }
    console.log("Done.");
}


/* 開始 */
remove_dirs();

(サンプル3) 複数のモジュール関数を使う例 (v10.x.x)

ここでは、複数のモジュール関数を使うやや複雑な例を示す。

サンプル1では常にエラーがないものとして、resolve() でペンディングを解消していたが、ここではエラー時には reject() でエラー処理をするようにした。

この例では、fs.open でファイルを開いて、fs.read で内容を読み込み、最後に fs.close する。

sample3.js
/* 非同期関数を使ってファイルを読む。*/
'use strict';

const fs = require("fs");

const PATH = "/home/user/bin/myuser";
const BUFFLEN = 1024;
var arraybuffer = new Array(1024);
var buffer = Buffer.from(arraybuffer);

/* ファイルを開く */
function open_file(path) {
    return new Promise((resolve, reject)=>{
        fs.open(path, "r", (err, fd)=>{
            if (err == null)
                resolve(fd);
            else
                reject(err);
        });
    });
}

/* バッファにファイル内容を読み込む。(バッファ長より短いファイルのみ) */
function read_fd(fd) {
    return new Promise((resolve, reject)=>{
        fs.read(fd, buffer, 0, BUFFLEN, null, (err, bytesRead, buffer)=>{
            if (err == null)
                resolve(bytesRead);
            else
                reject(err);
        });
    });
}

/* ファイルを閉じる */
function close_fd(fd) {
    return new Promise((resolve, reject)=>{
        fs.close(fd, (err)=>{
            if (err == null)
                resolve(null);
            else
                reject(err);
        });
    });
}

/* 非同期関数を呼び出してファイルの長さと内容を表示する。*/
async function read_file(path) {
    try {
      let fd = await open_file(PATH);
      let bytes = await read_fd(fd);
      console.log(bytes);
      let s = buffer.join(",");
      console.log(s);
      await close_fd(fd);
      console.log("Done.");
    }
    catch (e) {
      console.log(e.message);
    }
}

/* 実行開始 */
read_file(PATH);
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3