Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

歌舞伎座Tech Rx会

2,379 views

Published on

歌舞伎座Tech Rx会でのスライドです

Published in: Engineering

歌舞伎座Tech Rx会

  1. 1. With Rx by crexista
  2. 2. W H O • name: Kaoru Shibasaki • Twitter: @crexista
  3. 3. W H O • name: Kaoru Shibasaki • Twitter: @crexista • Work at:
  4. 4. With Rx
  5. 5. W H A T ’ S
  6. 6. W H A T ’ S • ブラウザのPushステートを使った非同期遷移 • FlashとJSでのExternalInterfaceを使った非同期通信 • 大量のAPI通信と配信サーバ等とのコネクション管理
  7. 7. 非同期処理の山
  8. 8. 非同期処理をどうやって Rxでさばいてたかを話します
  9. 9. A G E N D A 1. Why.(なぜ使ったのか) 2. Problem.(注意点は何か) 3. How.(解決策) 4. まとめ
  10. 10. W H Y 1. EventListenerではダメなのか? 2. MVVMフレームワークは? 3. その他
  11. 11. Reason 1. EventListenerではダメな理由
  12. 12. _result1 = null; _apiLoader.addEventListener(onLoad); _apiLoader.load(url); function onLoad(event){ _result1 = event.target.data; } 一般的なEventListener例 その1
  13. 13. _result1 = null; _result2 = null; _apiLoader.addEventListener(onLoad); _apiLoader.load(url); function onLoad(event){ _result1 = event.target.data; _apiLoader.removeEvent(onLoad); _apiLoader.addEventListener(onNext); _apiLoader.load(url); } function onNext(event) { _result2 = event.target.data; _apiLoader.removeEvent(onNext); } 一般的なEventListener例 その2 APIを順番に2つ リクエストする場合
  14. 14. _result1 = null; _result2 = null; _result3 = null; _apiLoader1.addEventListener(onLoad1); _apiLoader2.addEventListener(onLoad2); _apiLoader1.load(url); _apiLoader2.load(url); function onLoad1(event){ _result1 = event.target.data; _apiLoader.removeEvent(onLoad); _apiLoader.addEventListener(onNext); _apiLoader.load(url); if (_result2 != null) onLast(); } function onLoad2(event) { _result2 = event.target.data; _apiLoader.removeEvent(onNext); if (_result1 != null) onLast(); } function onLast() { //_result1と_result2を使った処理・・・ } 一般的なEventListener例 その3 APIを同時に2つリクエストして その2つの結果から新たに処理
  15. 15. 状 態 が 増 え る コ ー ル バ ッ ク 関 数 が 値 を 返 さ な い の で
  16. 16. テ ス ト が し づ ら い
  17. 17. var resultSignal = APILoader.load().map(onLoad).flatMap(nextLoad); resultSignal.subscribe(onNext, onComplete, onError); Rxだと・・・
  18. 18. var resultSignal = APILoader.load().map(onLoad).flatMap(nextLoad); resultSignal.subscribe(onNext, onComplete, onError); Rxだと・・・ 別クラスのstaticメソッドに出せる
  19. 19. テ ス ト が し や す い
  20. 20. var mockResult = Observable.returnValue(obj); mock(apiLoader.load).returnValue(mockResult); var testResult = APILoader.load().map().flatMap(); assertThat(testResult, isEqual(/*期待値*/)); Mockへに切り替えてのテスト
  21. 21. Reason 2. MVVMを採用しない理由
  22. 22. M V V M と は ? from wikiped
  23. 23. コ ー ド で 書 く と ?
  24. 24. コ ー ド 側V I E W 側 M V V M と は ? <tag value = “{uvm.name}"> <button click = "{uvm.updateAge}"> class UserViewModel { [Bindable] private var _age function updateAge { _age ++; } }
  25. 25. M V V M パ タ ー ン の 欠 点 • 揮発性イベントの扱いが面倒 • ViewとVMの紐付けを動的に変更できない • Viewロジックが複雑なものに向いてない
  26. 26. カ ス タ マ イ ズ コ ス ト が 高 い
  27. 27. Reason 3. その他
  28. 28. Operatorが多い
  29. 29. • 例) マウスオーバーしてから数秒間マウスアウトしなかった場合APIをリクエストする mouseOver .merge(mouseOut) .throttle(LIMIT_MSEC) .filter(checkEventType).subscribe(apiRequest) Rxでのイベントカスタマイズの例
  30. 30. P R O B L E M
  31. 31. P R O B L E M 1. 組み込み非同期処理との組み合わせ 2. PULL型からPUSH型のStreamへの変換時 3. SubscribeとError処理
  32. 32. 組 み 込 み 系 と の 相 性
  33. 33. どういうこと?
  34. 34. var request = apiResult.flatMap(function(result) { var socket = new Socket(); var obs = Observable.fromEvent(“connect”, socket); socket.connect(); return obs }) .map(function(event):void { var socket = event.target; //なんらかの処理 });
  35. 35. var request = apiResult.flatMap(function(result) { var socket = new Socket(); var obs = Observable.fromEvent(“connect”, socket); socket.connect(); return obs }) .map(function(event):void { var socket = event.target; //なんらかの処理 }); 組 み 込 み ク ラ ス
  36. 36. var request = apiResult.flatMap(function(result) { var socket = new Socket(); var obs = Observable.fromEvent(“connect”, socket); socket.connect(); return obs }) .map(function(event):void { var socket = event.target; //なんらかの処理 }); 組 み 込 み ク ラ ス ここでconnectしたsocketは requestをdisposeしても 消えない
  37. 37. var request = apiResult.flatMap(function(result) { var socket = new Socket(); var obs = Observable.fromEvent(“connect”, socket); socket.connect(); return obs }) .map(function(event):void { var socket = event.target; //なんらかの処理 }); 組 み 込 み ク ラ ス 組み込み系の非同期処理はそのまま使えない!
  38. 38. 解決策
  39. 39. カスタムシグナルを作る
  40. 40. Observable.create
  41. 41. 使い方
  42. 42. socketRequest = Observable.create(function(observer) { var socket = new Socket(); var func = function(event) { observer.onNext(event.target); observer.onComplete(); }; socket.addEventListener(“connect”,func); socket.connect(); return function() { socket.removeEventListener(func); if (!socket.connected) socket.close(); } });
  43. 43. socketRequest = Observable.create(function(observer) { var socket = new Socket(); var func = function(event) { observer.onNext(event.target); observer.onComplete(); }; socket.addEventListener(“connect”,func); socket.connect(); return function() { socket.removeEventListener(func); if (!socket.connected) socket.close(); } }); 接続完了時の コールバック
  44. 44. socketRequest = Observable.create(function(observer) { var socket = new Socket(); var func = function(event) { observer.onNext(event.target); observer.onComplete(); }; socket.addEventListener(“connect”,func); socket.connect(); return function() { socket.removeEventListener(func); if (!socket.connected) socket.close(); } }); 接続キャンセル時 コールバック
  45. 45. var request = apiResult.flatMap(function(result) { return socketRequest; }) .map(function(event):void { var socket = event.target; //なんらかの処理 });
  46. 46. P U L L 型 → P U S H 型
  47. 47. HOTとCOLDの話ではないです (近いけど)
  48. 48. • PULL型のStream • 非同期でリクエストし、値が取れたら完了 • 例) APIリクエストとか • PUSH型のStream • 一度subscribeすると永続的に値が来るStream • 例) UIとか、メールとか P U L L 型 → P U S H 型
  49. 49. A P I リ ク エ ス ト の 結 果 を も っ て P U S H 通 知 サ ー バ に 接 続 し た 例
  50. 50. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 }); 1. APIへのリクエスト結果
  51. 51. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 }); 2. Socketサーバへの リクエスト
  52. 52. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 }); 3. socketサーバからの PUSH通知
  53. 53. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  54. 54. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  55. 55. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  56. 56. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  57. 57. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  58. 58. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 });
  59. 59. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData; }).do(function(data) { //描画処理 }); 赤字のonDataは 上書きされたが connectionが closeされるわけではない
  60. 60. 対処法
  61. 61. apiResult.flatMap(function(result) { return socketRequest; }) .flatMap(function(socket) { return socket.onData.takeUntil(apiResult); }).do(function(data) { //描画処理 }); takeUntilを使って apiResultが完了したら onDataを止める
  62. 62. SubscribeとError処理
  63. 63. 1. SubscribeはaddEventListenerとだいたい同じ 2. Subscribeしまくる == eventListener書きまくり 3. eventの結果どこで何が起きるかわからなくなる! Subscribeについて
  64. 64. 悪 い 例 var request = APILoader.load(urlA).subscribe(function(result) { APILoader.load(result).subscribe(function() { //なんかの処理 }); }) ここの処理はキャンセル できない
  65. 65. 回 避 策 var innerReq; var request = APILoader.load(urlA).subscribe(function(result) { innerReq =APILoader.load(result).subscribe(function() { //なんかの処理 }); }) if (innerReq) innerReq.dispose(); request.dispose();
  66. 66. 回 避 策 var innerReq; var request = APILoader.load(urlA).subscribe(function(result) { innerReq =APILoader.load(result).subscribe(function() { //なんかの処理 }); }) if (innerReq) innerReq.dispose(); request.dispose(); できるといえばできるが テストが面倒に・・
  67. 67. 良 い 例 APILoader.load(urlA).flatMap(function(result) { return APILoader.load(result); }).subscribe(function() { //なんかの処理 });
  68. 68. • 変な書き方をするとメモリリークの原因に • というか、フラグが増えて管理が大変に • (副作用を起こすものなので当たり前といえば当たり前だが) Subscribeについて
  69. 69. • 変な書き方をするとメモリリークの原因に • というか、フラグが増えて管理が大変に • (副作用を起こすものなので当たり前といえば当たり前だが) Subscribeについて
  70. 70. • Rxではエラーが起きるとsubscribeが切れる • subscribeのonErrorを書き忘れるとハマル • なのできちんとonErrorかcatchErrorしましょう Errorについて
  71. 71. • Error処理忘れるな & Subscribeをあまり書くな • なのできちんとonErrorかcatchErrorしましょう SubscribeとErrorについて
  72. 72. で も 人 間 な の で 忘 れ る よ ね SubscribeとErrorについて
  73. 73. フ レ ー ム ワ ー ク 作 り ま し た
  74. 74. フ レ ー ム ワ ー ク の 方 針 • 薄くつくる • (現状の)レイヤードアーキテクチャに合わせる • 開発者はsubscribeを「基本」書かない • Error処理を書き忘れても最終的にcatchするように
  75. 75. ア ー キ テ ク チ ャ
  76. 76. 映 像 の 再 生 や 切 断 と い っ た ア プ リ と し て の 挙 動 を 決 め る 層 ア プ リ と し て の 挙 動 を 決 め る 際 に 必 要 と な る ビ ジ ネ ス ロ ジ ッ ク A P P L I C A T I O N と U I の つ な ぎ 込 み を す る 層
  77. 77. フ レ ー ム ワ ー ク の 方 針 • Presenter層でApplicationとUIのつなぎ込む • 開発者はつなぎ込みだけフレームワークに従う • あとは好きに書いてくれ
  78. 78. function start():Array { return [ [UI.onPlay.map(VideoModule.play), errorHandler], [MessageModule.comment.map(UI.addComment)], ]; } フレームワークでの実装例 UIとApplicationをつなぐ Signal
  79. 79. function start():Array { return [ [UI.onPlay.map(VideoModule.play), errorHandler], [MessageModule.comment.map(UI.addComment)], ]; } フレームワークでの実装例 このSignalの監視中のエラーの ハンドラー
  80. 80. function start():Array { return [ [UI.onPlay.map(VideoModule.play), errorHandler], [MessageModule.comment.map(UI.addComment)], ]; } フレームワークでの実装例 Error処理を変更する必要がない場合は そのまま
  81. 81. function run():Array { var arr = presenter.start(); for (var signals in arr) { var signal = signals[0]; var errorHandle = (signals.length==2)? signals[1]:defaultHandle signal.catchError(errorHandle) .subscribe(onNext, onComplete, onError); } } フレームワーク内部での処理 エラーのキャッチ漏れ防止機構
  82. 82. ま と め
  83. 83. ま と め • 基本クラスを覚える • Observable, Subject, Dispose, subscribeあたり • Operatorとしては以下を覚えておくと楽 • map, flatMap, filter, zip, merge, do, return, take, takeUntil
  84. 84. ま と め • Subscribeの扱いはルールを決めるべし • SubscribeをなくせばSignalの流れを掴みやすくなる
  85. 85. ま と め 流れ
  86. 86. – H T T P S : / / G I S T . G I T H U B . C O M / S T A L T Z / 8 6 8 E 7 E 9 B C 2 A 7 B 8 C 1 F 7 5 4 “Everything is a stream”

×