-->

Node.js

TypeScript 2.4.1 変更点 - Qiita
このエントリーをはてなブックマークに追加

さて、[TypeScript] ちょっとPromiseを使ってみるのサンプルをasync/await化してみる。
statPromise、readFilePromiseはそのまま使う。するとこんな感じに。

async function catAsyncTest(filename:string) {
    try {
        let stats = await statPromise(filename);
        if(stats.isFile()) {
            let data = await readFilePromise(filename);
            console.log(data);
        } else {
            console.log(filename + " is not a normal file.")
        }
    } catch(err) {
        console.log(err);
    }
}

fs.statSync、fs.readFileSyncって同期処理する関数を使った場合と同じような記述になる。

ちなみにfs.statSync、fs.readFileSyncを使って同期で処理するものはこんな感じになる。

function catSync(filename:string) {
    try {
        let stats = fs.statSync(filename);
        if(stats.isFile()) {
            let data = fs.readFileSync(filename, "utf-8");
            console.log(data);
        } else {
            console.log(filename + " is not a normal file.")
        }
    } catch(err) {
        console.log(err);
    }
}


このエントリーをはてなブックマークに追加

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のコールバック関数に渡されるようになる。



このエントリーをはてなブックマークに追加

ejsを使ってみる。ejsは、簡単に言うとjspやasp、PHPなんかのJavaScript版のようなものを実現するためのモジュールだ。文字列中の<%〜%>で囲まれた部分にJavaScriptを書いておくと、実行されて結果が埋め込まれた文字列が返ってくる。それをWebサーバの応答で返してやるとjspみたいなのができる。

$ npm install ejs

でモジュールのインストールする。

<%# コメント %>

コメント

<%= 変数 %>

のように書くと、渡した変数の内容がHTMLエスケープされて埋め込まれる。

<%- 変数 %>

のように書くと、渡した変数の内容がそのまま埋め込まれる。

<% 〜 %>

〜の部分にJavaScriptのコードが書ける。

サンプルだ。

index.ejs

<!DOCTYPE html>
<html lang="ja">
<head>
	<meta charset="utf-8">
	<title><%= title %></title>
</head>
<body
	<%# コメント %>
	
	<%# hogeの内容をHTMLエスケープして埋め込まれる %>
	<p><%= hoge %></p>
		
	<%# hoge2の内容がそのまま埋め込まれる %>
	<%- hoge2 %>
	
	<%# 埋め込まれているスクリプトが実行される %>
	<ul>
	<% hoge3.forEach(function(item) { %>
		<li><%= item %></li>
	<%	});	%>
	</ul>
</body>
</html>

test.ts

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

var ejs = require("ejs");

http.createServer((req, res)=>{
	var tmp = ejs.render(fs.readFileSync("./index.ejs", "utf-8"),
		{ 
			"title":"タイトル",
			"hoge":"<h1>ほげー<h1>",
			"hoge2":"<h1>H1</h1>",
			"hoge3":[ "ほげ1", "ほげ2", "ほげ3" ]
		}
	);
	res.writeHead(200, { "Content-Type": "text/html"});
	res.write(tmp);
	res.end();
}).listen(9999);

ejs.renderに文字列とejsで使っている変数名を持っているオブジェクトを渡すとこんな感じになる。

56


このエントリーをはてなブックマークに追加

起動時の引数でしたモジュールを読み込んで関数を呼び出してみる。まー、わざわざ書くほどのものではないんだが。

これで、プラグインみたいなのが作れるってことだな。

test.ts

/// <reference path="./typings/node/node.d.ts" />

var test:any = null;
try {
	test = require(process.argv[2]);
} catch(err) {
	console.log(err);
}

if(test) {
	if(test.print) {
		test.print();	
	} else {
		console.log("printメソッドが実装されていません。");
	}
}

test1.ts

export function print() {
	console.log("test1 !!");
}

test2.ts

export function print() {
	console.log("test2 !!");
}

tsconfig.json

{
	"compilerOptions": {
		"module": "commonjs",
		"target": "es5"
	},
	"files":[
		"./test.ts",
		"./test1.ts",
		"./test2.ts"
	]
}

tscでコンパイルする。
node ./test.ts ./test1 と実行すると "test1 !!"と
node ./test.ts ./test2 と実行すると "test2 !!”と表示される。



このエントリーをはてなブックマークに追加

↑このページのトップヘ