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

2,821 views
2,694 views

Published on

大阪Node学園 六時限目 「generator小咄」のスライドです

サンプルコードはgithubにあります
https://github.com/craftgear/ong6

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,821
On SlideShare
0
From Embeds
0
Number of Embeds
20
Actions
Shares
0
Downloads
11
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

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

  1. 1. 大阪Node学園六時限目 generator小咄 渡辺俊輔@craftgear http://blog.craftgear.net/
  2. 2. Agenda 1. generator = 同期風コード? 2. generatorってなんだ 3. generatorとiterator 4. 同期風コードの書き方 5. generatorベースのライブラリ群 6. generatorを使うにあたって考慮したいこと 7. 今日のまとめ 8. 今後の予定 9. 参考文献 10. About the Author
  3. 3. generator = 同期風コード?
  4. 4. とあるブログにて nodev0.12 (= nodev1.0 ?)からgenerator が使えるよ うになるらしい。 ほほう、どれどれ ( ´_ゝ`)
  5. 5. ブログ記事の要約(IMHO) ここにgeneratorがある ゃろ? ( ^ω^) generator これをこうして… ( ^ω^) ≡ ≡ こう ゃ ( ^ω^) 同期っぽい!
  6. 6. ( ゚д゚)え?
  7. 7. generator ≠ 同期風コード generator +α =同期風コード
  8. 8. generatorってなんだ
  9. 9. 関数の一種 generatorもしくはgenerator関数とも 通常の関数 ↓ generator function* ←アスタリスク重要! 途中で値を返すのにはyieldを使う function hoge() { return 'hoge'; } 1 2 3 function* hoge() { yield 'hoge'; } 1 2 3
  10. 10. nodev0.11.x ではオプションが必要 node hoge.js ↓ node --harmony-generators hoge.js
  11. 11. 戻り値が違う 通常の関数 実行結果 ↓ 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
  12. 12. generatorは関数の一種 アスタリスク重要 途中で値を返すのには yield を使う nodev0.11.x ではオプションが必要 戻り値がiteratorオブジェクト
  13. 13. generatorとiterator
  14. 14. iteratorとは GoFのあれ iteratorもしくはgenerator iteratorとも generatorを実行した時の戻り値
  15. 15. 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
  16. 16. 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
  17. 17. その逆も出来る 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
  18. 18. 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文の評価結 果、この二つの区別が大事
  19. 19. iteratorはgeneratorのリモコン next()はリモコンの再生ボタン next()の戻り値はgeneratorの内部状態 next()メソッドに引数を渡すと、generator内部 に値を送り込める yieldが返す値とyield文の評価結果、この二 つの区別が大事
  20. 20. 同期風コードの書き方
  21. 21. コールバックスタイル 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
  22. 22. 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 同期風コード
  23. 23. 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
  24. 24. generatorで同期風コードを実現するには generatorとiteratorを操作するコードの二つが 必要 generator内部のコード&自分が実行したい処理' iteratorを回すコード&generatorを制御するコード' の二つに分けて考えるようにするとわかりやすい +αの中身はiteratorの操作コード
  25. 25. generatorスタイル&仮'の問題点 iteratorを制御するコードを書かないといけない yieldが一回しか使えない エラー処理がない しかしこれらの処理は一度書けばすむは ↓ それnpmで
  26. 26. generatorで同期風コードを実現するには generatorとiteratorを操作するコードの二つが 必要 こうしてこう ゃ の中身はiteratorの操作 コード 自分で書か にライブラリを使おう!
  27. 27. generatorベースのライブラリ群
  28. 28. その1 - suspend https://github.com/jmar777/suspend
  29. 29. 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
  30. 30. suspendの特徴 generatorを引数に渡して実行した後、再度実行する必要が ある (16行目) nodeの標準コールバックスタイルがそのまま使える Promiseとも併用できる スタックトリースでエラーを起こしたyield文の場所がわかる try catchするとエラーの場所がわからなくなる
  31. 31. その2 - co https://github.com/visionmedia/co
  32. 32. 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
  33. 33. coの特徴 呼び出す関数をラップするか、もしくはThunk(次ページ参照) にする必要がある &4行目' Promiseとも併用できる generatorを引数に渡すだけでよい スタックトリースでエラーの場所がわからない expressのミドルイェア用モジュールがある TJイェア
  34. 34. 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); }; }
  35. 35. その3 - genny https://github.com/spion/genny
  36. 36. 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
  37. 37. gennyの特徴 suspendと似ているが最後の関数実行は必要ない &18行目' そのかわりコールバック用関数を実行して渡す必要がある &11行目' ThunkやPromiseとも併用できる スタックトリースでエラーを起こしたyield文の場所がわかる try catchするとエラーの場所がわからなくなる longStackTraceオプションを有効にするとtry catchしてもエラ ーの場所がわかる ただしlongStackTraceオプションはオーバーヘッドがすごい expressのミドルイェアをgeneratorにできる genny.middleware がある
  38. 38. その4 - gen-run https://github.com/creationix/gen-run
  39. 39. 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
  40. 40. gen-runの特徴 coと同 ようなThunkとsuspendのようなコールバックスタイル の両方が使える スタックトリースでエラーを起こしたyield文の場所がわかる try catchするとエラーの場所がわからなくなる
  41. 41. generatorベースのライブラリを使うと 同期風コードが書ける try catchでエラー処理が書ける デバッグがしづらくなる
  42. 42. generatorを使うにあたって考慮し たいこと
  43. 43. nodeの方針 by Isaac コールバックスタイルの理解は必須 The Future of Programming in Node.js "Callbacks will remain the de facto way to implement asynchrony. Generators and Promises are interesting and will remain a userland option." 訳: コールバックがデファクトの非同期実 装方法で在り続けます。ジェネリータやプロ ミスは興味深いですが、ユーザーランドの選 択肢のままです。
  44. 44. ライブラリのオーバーヘッド gennyの作者が書いたライブラリの比較記事 各ライブラリの実行速度、デバッグのしやすさなどあり ライブラリごとにばらつきはあるが、リクエストごとの処理時間で 160%〜400%、メモリ使用量で140%〜240%のオーバーヘッ ドあり generatorとは関係ないけどPromiseベースのライブラリが異常 に遅い オーバーヘッドあり Analysis of generators and other async patterns in node
  45. 45. コールバックは捨てられない ライブラリ利用のオーバーヘッドを織り込 んでおく
  46. 46. 今日のまとめ
  47. 47. generatorベースのライブラリを使うと同期風 コードが書ける ライブラリを使うだけならgeneratorそのもの を理解してなくても大丈夫 generatorベースのライブラリを使うとオーバ ーヘッドがある 他のライブラリと同 ように便利なら使う プロジェクトごとに非同期処理の方針が あるといいかも
  48. 48. 今後の予定 大阪Node学園七時限目 ゼロから始めるnode.js 大阪Node学園八時限目 タイトル未定 9/28 JAWS FESTA Kansai 2013 出張版 10/28 Innovation EGG 第一回勉強会 出張版
  49. 49. 参考文献
  50. 50. イテリータとジェネリータ - JavaScript | MDN generators in v8 -- wingolog A Study on Solving Callbacks with JavaScript Generators A Closer Look at Generators Without Promises jmar777/suspend visionmedia/co spion/genny creationix/gen-run The Future of Programming in Node.js - Google グループ Analysis of generators and other async patterns in node
  51. 51. About the Author 渡辺俊輔 フリーランスWebエンジニア 質問、訂正などはgoogle+、twitterまたはメールでどう google+ 大阪node学園 twitter@craftgear watanabe@craftgear.net

×