Your SlideShare is downloading. ×
歌舞伎座Tech Rx会
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

歌舞伎座Tech Rx会

451
views

Published on

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

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

Published in: Engineering

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
451
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
4
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

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