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

#(サンプル１) 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() をコールして処理を実行している。


#(サンプル２） fsPromise 関数を使う場合 (v12.x.x以上)
v10.x.x までは fsPromise 関数は正式にサポートされてないため、警告が出るが、v12.x.x では正式サポートされたようである。

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

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

fsPromise モジュール関数では、関数自体が Promise オブジェクトを返すので、ラッパー関数 (サンプル１の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();

```


#(サンプル３) 複数のモジュール関数を使う例 (v10.x.x)
ここでは、複数のモジュール関数を使うやや複雑な例を示す。

サンプル１では常にエラーがないものとして、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);
```