大阪Node学園 六時限目 「generator小咄」

Post on 02-Dec-2014

3.376 views 1 download

description

大阪Node学園 六時限目 「generator小咄」のスライドです サンプルコードはgithubにあります https://github.com/craftgear/ong6

Transcript of 大阪Node学園 六時限目 「generator小咄」

大阪Node学園六時限目

generator小咄

渡辺俊輔 @craftgearhttp://blog.craftgear.net/

Agenda1. generator = 同期風コード?

2. generatorってなんだ

3. generatorとiterator4. 同期風コードの書き方

5. generatorベースのライブラリ群

6. generatorを使うにあたって考慮したいこと

7. 今日のまとめ

8. 今後の予定

9. 参考文献

10. About the Author

generator = 同期風コード?

とあるブログにて

node v0.12 (= node v1.0 ?) からgenerator が使えるよ

うになるらしい。

ほほう、どれどれ

( ´_ゝ`)

ブログ記事の要約(IMHO)

ここにgeneratorがある ゃろ?

( ^ω^)generator

これをこうして…( ^ω^)

≡ ≡

こう ゃ

( ^ω^)同期っぽい!

( ゚д゚)え?

generator ≠ 同期風コード

generator + α = 同期風コード

generatorってなんだ

関数の一種

generatorもしくはgenerator関数とも

通常の関数

generator

function* ←アスタリスク重要!途中で値を返すのにはyieldを使う

function hoge() {

return 'hoge';

}

1

2

3

function* hoge() {

yield 'hoge';

}

1

2

3

node v0.11.x ではオプションが必要

node hoge.js↓

node --harmony-generators hoge.js

戻り値が違う

通常の関数 実行結果

generator 実行結果

戻り値がiteratorオブジェクト

&iteratorについてはのちほど'

function hoge() {

return 'hoge';

}

var result = hoge();

console.log(result);

1

2

3

4

5

6

hoge1

function* hoge() {

yield 'hoge';

}

var result = hoge();

console.log(result, typeof result);

1

2

3

4

5

6

{} object1

https://github.com/craftgear/ong6/tree/master/example/01_return_value.js

generatorは関数の一種

アスタリスク重要

途中で値を返すのには yield を使う

node v0.11.x ではオプションが必要

戻り値がiteratorオブジェクト

generatorとiterator

iteratorとは

GoFのあれ

iteratorもしくはgenerator iteratorとも

generatorを実行した時の戻り値

iteratorはgeneratorを操作するためのもの

next() メソッドを呼び出すと yieldまで処理が走って止まる

処理を再開するには再度next()を呼ぶ

サンプルコード

iteratorはgeneratorのリモコン

next() メソッドはリモコンの再生ボタン

function* hoge(){

yield 1;

yield 2;

//return 3;

}

var iterator = hoge();

console.log(iterator.next());

console.log(iterator.next());

console.log(iterator.next());

1

2

3

4

5

6

7

8

9

10

11

https://github.com/craftgear/ong6/tree/master/example/02_iterator.js

next()でgeneratorの内部状態がわかる

next()の戻り値は value と done の二つのプロパティを持ったオ

ブジェクト

value ヹヹヹ yield の右側の値が入る

done ヹヹヹ generatorの最後に達するか、明示的にreturn 文に出会うと true になる

サンプルコード 実行結果

next()メソッドの戻り値で、generator内部の状

態がわかる

function* hoge(){

yield 1;

yield 2;

//return 3;

}

var iterator = hoge();

console.log(iterator.next());

console.log(iterator.next());

console.log(iterator.next());

1

2

3

4

5

6

7

8

9

10

11

{ value: 1, done: false }

{ value: 2, done: false }

{ value: undefined, done: true }

//return 3;のコメントアウトを外した場合

{ value: 1, done: false }

{ value: 2, done: false }

{ value: 3, done: true }

1

2

3

4

5

6

7

8

https://github.com/craftgear/ong6/tree/master/example/02_iterator.js

その逆も出来る

generator内部の状態を知るだけでなく、next()メソッドで

generator内部の状態を操作できる

サンプルコード

実行結果

next()メソッドに引数を渡すと、generatorに値

を送りこめる

function* hoge(){

var x = null;

console.log('inside generator: x is ', x);

x = yield 1;

console.log('inside generator: after yielded, x is ', x);

}

var iterator = hoge();

console.log('return value of next', iterator.next());

console.log('return value of next', iterator.next('hoge'));

1

2

3

4

5

6

7

8

9

10

11

inside generator: x is null

return value of next { value: 1, done: false }

inside generator: after yielded, x is hoge

return value of next { value: undefined, done: true }

1

2

3

4

https://github.com/craftgear/ong6/tree/master/example/03_next.js

next()を呼び出した時に起こること

iterator操作側: 10行目 next()メソッドを呼び出す

generator内部: yieldのある行(4行目)まで実行され、

yieldの右側の値が戻り値のvalueプロパ

ティとして返される

処理がここでとまる

iteretor操作側: 11行目でふたたびnext()メソッドを呼び

出す

generator内部: 先ほど止まったyieldのある行(4行目)から実行が再開される。このとき、nextの引

数が yield文の評価結果になる

すなわち、4行目のyield 1 が hoge におき

かわり、xにhogeが代入される

yieldが返す値とyield文の評価結

果、この二つの区別が大事

iteratorはgeneratorのリモコン

next() はリモコンの再生ボタン

next() の戻り値はgeneratorの内部状態

next() メソッドに引数を渡すと、generator内部

に値を送り込める

yieldが返す値とyield文の評価結果、この二

つの区別が大事

同期風コードの書き方

コールバックスタイル

var fs = require('fs');

fs.readFile(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

, function (error, result) {

console.log(result);

process.exit();

}

);

1

2

3

4

5

6

7

8

9

10

https://github.com/craftgear/ong6/tree/master/example/04_async.js

generatorスタイル&仮'

var fs = require('fs');

var fileReader = function* (callback){

var content = yield fs.readFile(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

, callback

);

console.log('generator: ', content);

process.exit();

}

function run(generator){

var iterator = null;

var callback = function(err, data) {

if(err) iterator.throw(err);

iterator.next(data);

};

iterator = generator(callback);

iterator.next();

}

run(fileReader);

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

https://github.com/craftgear/ong6/tree/master/example/05_sync.js

同期風コード

generatorスタイル&仮'処理の流れ

1. 25行目から処理開始

2. 21行目でgeneratorにコールバック用関数を渡してiterator生成

3. 22行目のiterator.next()で4行目のyieldの右辺が実行され

4. fs.readFile処理が走り、16行目のコールバック用の関数に結

果が渡る

5. 18行目でiterator.nextを呼び出す、読み込んだファイルの中

身を引数に渡して呼び出す

6. 4行目から処理が再開され、resultには読み込んだファイルの

中身が入る

7. 9行目で結果を表示する

8. 10行目で処理終了

https://github.com/craftgear/ong6/tree/master/example/05_sync.js

generatorで同期風コードを実現するには

generatorとiteratorを操作するコードの二つが

必要

generator内部のコード&自分が実行したい処理'

iteratorを回すコード&generatorを制御するコード'

の二つに分けて考えるようにするとわかりやすい

+αの中身はiteratorの操作コード

generatorスタイル&仮'の問題点

iteratorを制御するコードを書かないといけない

yieldが一回しか使えない

エラー処理がない

しかしこれらの処理は一度書けばすむは

それnpmで

generatorで同期風コードを実現するには

generatorとiteratorを操作するコードの二つが

必要

こうしてこう ゃ の中身はiteratorの操作

コード

自分で書か にライブラリを使おう!

generatorベースのライブラリ群

その1 - suspendhttps://github.com/jmar777/suspend

suspendを使った同期風コード

var suspend = require('suspend')

, fs = require('fs');

suspend(function* (resume){

try{

var content = yield fs.readFile(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

, resume

);

console.log('generator: ', content);

}

catch(e){

console.log(e);

}

})();

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

https://github.com/craftgear/ong6/tree/master/example/06_suspend.js

suspendの特徴

generatorを引数に渡して実行した後、再度実行する必要が

ある (16行目)nodeの標準コールバックスタイルがそのまま使える

Promiseとも併用できる

スタックトリースでエラーを起こしたyield文の場所がわかる

try catchするとエラーの場所がわからなくなる

その2 - cohttps://github.com/visionmedia/co

coを使った同期風コード

var co = require('co')

, fs = require('fs');

var read = co.wrap(fs.readFile);

co(function* (){

try{

var content = yield read(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

);

console.log('generator: ', content);

}

catch(e){

console.log(e);

}

});

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

https://github.com/craftgear/ong6/tree/master/example/07_co.js

coの特徴

呼び出す関数をラップするか、もしくはThunk(次ページ参照)にする必要がある &4行目'

Promiseとも併用できる

generatorを引数に渡すだけでよい

スタックトリースでエラーの場所がわからない

expressのミドルイェア用モジュールがある

TJイェア

Thunkってなに?

A "thunk" is also known as a "continuable"and is simply a function that accepts a node

style callback as it's only argument.訳: "thunk"は"continuable"ともいわれ、

node形式のコールバックのみを引数として

け る関数です。

gen-runのREADME.mdより

function sleep(ms) {

return function (callback) {

setTimeout(callback, ms);

};

}

その3 - gennyhttps://github.com/spion/genny

gennyを使った同期風コード

var genny = require('genny')

, fs = require('fs');

//genny.longStackSupport = true;

genny.run(function* (resume){

try{

var content = yield fs.readFile(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

, resume()

);

console.log('generator: ', content);

}

catch(e){

console.log(e);

}

});

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

https://github.com/craftgear/ong6/tree/master/example/08_genny.js

gennyの特徴

suspendと似ているが最後の関数実行は必要ない &18行目'

そのかわりコールバック用関数を実行して渡す必要がある

&11行目'

ThunkやPromiseとも併用できる

スタックトリースでエラーを起こしたyield文の場所がわかる

try catchするとエラーの場所がわからなくなる

longStackTraceオプションを有効にするとtry catchしてもエラ

ーの場所がわかる

ただしlongStackTraceオプションはオーバーヘッドがすごい

expressのミドルイェアをgeneratorにできる

genny.middleware がある

その4 - gen-runhttps://github.com/creationix/gen-run

gen-runを使った同期風コード

var run = require('gen-run')

, fs = require('fs');

run(function* (resume){

try{

var content = yield fs.readFile(

__dirname + '/ong6.txt'

, {encoding: 'utf-8'}

, resume()

);

console.log('generator: ', content);

}

catch(e){

console.log(e);

}

});

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

https://github.com/craftgear/ong6/tree/master/example/09_gen-run.js

gen-runの特徴

coと同 ようなThunkとsuspendのようなコールバックスタイル

の両方が使える

スタックトリースでエラーを起こしたyield文の場所がわかる

try catchするとエラーの場所がわからなくなる

generatorベースのライブラリを使うと

同期風コードが書ける

try catchでエラー処理が書ける

デバッグがしづらくなる

generatorを使うにあたって考慮し

たいこと

nodeの方針

by Isaac

コールバックスタイルの理解は必須

The Future of Programming in Node.js

"Callbacks will remain the de facto way toimplement asynchrony. Generators and

Promises are interesting and will remain auserland option."

訳: コールバックがデファクトの非同期実

装方法で在り続けます。ジェネリータやプロ

ミスは興味深いですが、ユーザーランドの選

択肢のままです。

ライブラリのオーバーヘッド

gennyの作者が書いたライブラリの比較記事

各ライブラリの実行速度、デバッグのしやすさなどあり

ライブラリごとにばらつきはあるが、リクエストごとの処理時間で

160%〜400%、メモリ使用量で140%〜240%のオーバーヘッ

ドあり

generatorとは関係ないけどPromiseベースのライブラリが異常

に遅い

オーバーヘッドあり

Analysis of generators and other async patterns in node

コールバックは捨てられない

ライブラリ利用のオーバーヘッドを織り込

んでおく

今日のまとめ

generatorベースのライブラリを使うと同期風

コードが書ける

ライブラリを使うだけならgeneratorそのもの

を理解してなくても大丈夫

generatorベースのライブラリを使うとオーバ

ーヘッドがある

他のライブラリと同 ように便利なら使う

プロジェクトごとに非同期処理の方針が

あるといいかも

今後の予定

大阪Node学園七時限目 ゼロから始めるnode.js

大阪Node学園八時限目 タイトル未定

9/28 JAWS FESTA Kansai 2013 出張版

10/28 Innovation EGG 第一回勉強会 出張版

参考文献

About the Author

渡辺俊輔

フリーランスWebエンジニア

質問、訂正などはgoogle+、twitterまたはメールでどう

google+ 大阪node学園

twitter@craftgearwatanabe@craftgear.net