async/awaitを使ってみようと調べてみるとPromiseが知らないといけないみたいなんでちょっと調べてみた。

Promise使うと非同期処理のコールバックのネストが深くなるのを少しいい感じにしてくれるものだ。最初に使ってみようとしてたasync/awaitはさらに見た目がいい感じになるはずw

Promise化する前の非同期処理のプログラムを示す。

/// <reference path="./typings/index.d.ts" />
import * as fs from "fs";

const filename = "./test.txt";

function cat(filename:string):void {
	// ファイルの情報を取ってくる
    fs.stat(filename, (err, stats)=>{
        if(err) {
            console.log(err);
            return;
        }
        if(stats.isFile()) {		// 通常ファイルなら
        	// ファイルを読みこむ
            fs.readFile(filename, "utf-8", (err, data)=>{
                if(err) {
                    console.log(err);
                    return;
                }
                // 内容を表示
                console.log(data);
            });
        } else {
            console.log(filename + " is not a normal file.")
        }
    });
}

cat(filename);

さぁ、これをPromise化してみようw

試した環境はこんな感じ。
node.js v7.10.0
TypeScript Version 2.3.2

環境を整える。 tsconfig.jsonでtargetをes6にするか、
targetをes5でlibに["dom", "es2015.promise", "es5"]を指定する。これはasync/awaitを使ったプログラムをes5に変換するのに必要な設定。

fs.statをPromise化。

function statPromise(filename:string):Promise<fs.Stats> {
    return new Promise<fs.Stats>((resolve, reject)=>{
        fs.stat(filename, (err, stats)=>{
            if(err) {
                reject(err);
            } else {
                resolve(stats);
            }
        });
    });
}

Promiseのインスタンスを作って返す。コールバック関数でfs.statを呼びその非同期処理でerrならrejectを成功したらresolveを呼んでやります。これでこんな風に使うと

statPromise("hoge")
	.then(stats=>{ console.log(stats); })
	.catch(err=>{ console.log(err); });

fs.statが成功するとthenに与えた関数にstatsを引数にして呼び出され、失敗するとcatchに与えた関数にerrを引数にして呼び出される。
あと、この処理は、わざと冗長に書いたが

statPromise("hoge")
	.then(console.log)
	.catch(console.log);

でいい。

fs.statと同じように、fs.readFileをPromise化。

function readFilePromise(filename:string):Promise<string> {
    return new Promise<string>((resolve, reject)=>{
        fs.readFile(filename, "utf-8", (err, data)=>{
            if(err) {
                reject(err);
            } else {
                resolve(data);
            }
        });
    });
}

catPromiseをこれらを使って作る。

function catPromise(filename:string):void {
    statPromise(filename).then(stats=>{
        if(stats.isFile()) {
            return readFilePromise(filename);
        }
        return Promise.reject(new Error(filename + " is not a normal file."))
    }).then(data=>{
        console.log(data);
    }).catch(err=>{
        console.log(err);
    });
}

コールバックのネストが平らになる。もっと深いやつならもっとわかりやすいんだろな。
1つめのthenのコールバック関数で別のPromiseのインスタンスを返すと、その結果が次のthenのコールバック関数に渡されるようになる。