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

51
大阪Node 学園六時限目 generator 小咄䛃 渡辺俊輔 @craftgear http://blog.craftgear.net/

Upload: shunsuke-watanabe

Post on 02-Dec-2014

3.376 views

Category:

Technology


1 download

DESCRIPTION

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

TRANSCRIPT

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

大阪Node学園六時限目

generator小咄

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

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

Agenda1. generator = 同期風コード?

2. generatorってなんだ

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

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

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

7. 今日のまとめ

8. 今後の予定

9. 参考文献

10. About the Author

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

generator = 同期風コード?

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

とあるブログにて

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

うになるらしい。

ほほう、どれどれ

( ´_ゝ`)

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

ブログ記事の要約(IMHO)

ここにgeneratorがある ゃろ?

( ^ω^)generator

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

≡ ≡

こう ゃ

( ^ω^)同期っぽい!

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

( ゚д゚)え?

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

generator ≠ 同期風コード

generator + α = 同期風コード

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

generatorってなんだ

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

関数の一種

generatorもしくはgenerator関数とも

通常の関数

generator

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

function hoge() {

return 'hoge';

}

1

2

3

function* hoge() {

yield 'hoge';

}

1

2

3

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

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

node hoge.js↓

node --harmony-generators hoge.js

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

戻り値が違う

通常の関数 実行結果

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

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

generatorは関数の一種

アスタリスク重要

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

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

戻り値がiteratorオブジェクト

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

generatorとiterator

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

iteratorとは

GoFのあれ

iteratorもしくはgenerator iteratorとも

generatorを実行した時の戻り値

Page 15: 大阪Node学園 六時限目 「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

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

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

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

その逆も出来る

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

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

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文の評価結

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

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

iteratorはgeneratorのリモコン

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

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

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

に値を送り込める

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

つの区別が大事

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

同期風コードの書き方

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

コールバックスタイル

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

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

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

同期風コード

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

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

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

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

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

必要

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

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

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

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

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

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

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

yieldが一回しか使えない

エラー処理がない

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

それnpmで

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

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

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

必要

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

コード

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

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

generatorベースのライブラリ群

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

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

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

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

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

suspendの特徴

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

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

Promiseとも併用できる

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

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

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

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

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

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

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

coの特徴

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

Promiseとも併用できる

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

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

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

TJイェア

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

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);

};

}

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

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

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

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

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

gennyの特徴

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

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

&11行目'

ThunkやPromiseとも併用できる

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

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

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

ーの場所がわかる

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

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

genny.middleware がある

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

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

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

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

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

gen-runの特徴

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

の両方が使える

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

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

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

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

同期風コードが書ける

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

デバッグがしづらくなる

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

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

たいこと

Page 43: 大阪Node学園 六時限目 「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."

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

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

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

択肢のままです。

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

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

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

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

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

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

ドあり

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

に遅い

オーバーヘッドあり

Analysis of generators and other async patterns in node

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

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

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

んでおく

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

今日のまとめ

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

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

コードが書ける

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

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

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

ーヘッドがある

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

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

あるといいかも

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

今後の予定

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

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

9/28 JAWS FESTA Kansai 2013 出張版

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

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

参考文献

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

About the Author

渡辺俊輔

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

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

google+ 大阪node学園

twitter@[email protected]