Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RxJS - The Art of Abstraction

Jerry Hong
November 01, 2019

RxJS - The Art of Abstraction

Jerry Hong

November 01, 2019
Tweet

More Decks by Jerry Hong

Other Decks in Programming

Transcript

  1. Jerry Hong Tech Leader | Website: blog.jerry-hong.com Facebook: J.H.blog.tw Branch8

    2019 ModerWeb speaker 2018 FEDC organiser 2017 JSDC.tw speaker 2017 RxJS 30天鐵⼈人賽冠軍 2016 JSDC.tw speaker
  2. let isRequesting = false; scrollView.addEventListener('scroll', event => { const DOM

    = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } });
  3. let isRequesting = false; scrollView.addEventListener('scroll', event => { const DOM

    = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); 無限滾動 Infinite scroll
  4. let isRequesting = false; scrollView.addEventListener('scroll', event = const DOM =

    event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; 註冊滾動事件 Listen scroll event
  5. let isRequesting = false; scrollView.addEventListener('scroll', event = const DOM =

    event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); 判斷滾動⾼高度 Determine scroll height
  6. scrollView.addEventListener('scroll', event = const DOM = event.target; if(hasScrolled(DOM) > 0.9

    && !isRequesting) isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); 發送 Request
  7. 設定 Flag let isRequesting = false; scrollView.addEventListener('scroll', event = const

    DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting = true; fetch('url...') .then(res => {
  8. let isRequesting = false; scrollView.addEventListener('scroll', event = const DOM =

    event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); 若若 isRequesting 為 false 
 再發送 Request
  9. let isRequesting = false; scrollView.addEventListener('scroll', event = const DOM =

    event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } 發送 Request 前, 設定 isRequest 為 true
  10. const DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) isRequesting

    = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); Response 後, 設定 isRequest 為 false
  11. let isRequesting = false; scrollView.addEventListener('scroll', event => { const DOM

    = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); 無限滾動 Infinite scroll
  12. let isRequesting = false; scrollView.addEventListener('scroll', event => { const DOM

    = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); 兩兩個非同步⾏行行為
  13. let isRequesting = false; scrollView.addEventListener('scroll', event => { const DOM

    = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } }); 同樣是非同步⾏行行為
 卻⽤用不同的 pattern
  14. Flag let isRequesting = false; scrollView.addEventListener('scroll', event => { const

    DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; }); } });
  15. More Flags let isRequesting = false; let requestCount = 0;

    const scrollHandler = function(event) { const DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; requestCount = requestCount + 1; if (requestCount === 3) { scrollView.removeEventListener('scroll', scrollHandler) } }) } } scrollView.addEventListener('scroll', scrollHandler);
  16. let isRequesting = false; let requestCount = 0; const scrollHandler

    = function(event) { const DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; requestCount = requestCount + 1; if (requestCount === 3) { scrollView.removeEventListener('scroll', scrollHandler) } }) } } scrollView.addEventListener('scroll', scrollHandler); 我們⼀一定要寫
 這麼醜的程式碼嗎?
  17. let isRequesting = false; let requestCount = 0; const scrollHandler

    = function(event) { const DOM = event.target; if(hasScrolled(DOM) > 0.9 && !isRequesting) { isRequesting = true; fetch('url...') .then(res => { // do something change view isRequesting = false; requestCount = requestCount + 1; if (requestCount === 3) { scrollView.removeEventListener('scroll', scrollHandler) } }) } } scrollView.addEventListener('scroll', scrollHandler); fromEvent(scrollView, 'scroll') .pipe( map(event "=> event.target), map(hasScrolled), filter(p "=> p > 0.9), exhaustMap(() "=> ajax('url""...')), take(3) ) .subscribe(res "=> { "// do something change view }) VanillaJS RxJS
  18. var mouseMove = fromEvent(DOM, 'mousemove'); observable 建立與訂閱 var subscription =

    mouseMove .subscribe(x => console.log(x)); subscription.unsubscribe();
  19. import { from } from 'rxjs'; import { map, filter

    } from 'rxjs/operators'; var sub = from([1, 2, 3]) .pipe( map(x "=> x + 1), filter(x "=> x % 2 ""=== 0) ); .subscribe({ next: x "=> console.log(x), error: err "=> {}, complete: () "=> {}, });
  20. observable • Observable 的物件實例例 • 在未被訂閱之前,只是 個物件,不會有任何 Side Effect •

    可被訂閱(subscribe) import { from } from 'rxjs'; import { map, filter } from 'rxjs/operators var sub = from([1, 2, 3]) .pipe( map(x "=> x + 1), filter(x "=> x % 2 ""=== 0) ); .subscribe({ next: x "=> console.log(x), error: err "=> {},
  21. operator • ⼀一個 function 傳入 observable 回傳 observable • 可對元素作運算處理理

    import { from } from 'rxjs'; import { map, filter } from 'rxjs/operators var sub = from([1, 2, 3]) .pipe( map(x "=> x + 1), filter(x "=> x % 2 ""=== 0) ); .subscribe({ next: x "=> console.log(x), error: err "=> {}, complete: () "=> {}, });
  22. observer • ⼀一個 object • 具有 next, error, complete •

    ⽤用來來訂閱 observable map(x "=> x + 1), filter(x "=> x % 2 ""=== 0) ); .subscribe({ next: x "=> console.log(x), error: err "=> {}, complete: () "=> {}, });
  23. subscription • observable 訂閱後回傳 的物件 • 可拿來來退訂 (unsubscribe) • 可以其他

    subscription 合併 import { from } from 'rxjs'; import { map, filter } from 'rxjs/operators var sub = from([1, 2, 3]) .pipe( map(x "=> x + 1), filter(x "=> x % 2 ""=== 0) ); .subscribe({ next: x "=> console.log(x), error: err "=> {}, complete: () "=> {}, });
  24. Marble Diagram - ⼀一⼩小段時間 (10 frames) n(0-9/a-z) 送出的元素(next) | 送出結束

    (complete) # 送出錯誤 (error) () 同步送出 time ----0---1---2---3-- ----0---1---2---3| ----0---1---2---3--# (123|)
  25. interval(10) .pipe( take(3), map(x "=> x + 1), filter(x "=>

    x % 2 ""=== 1) ) -01234.. -012| -1-(3|) -01(2|) -12(3|)
  26. fromEvent(scrollView, 'scroll') .pipe( map(event "=> event.target), map(hasScrolled), filter(p "=> p

    > 0.9), exhaustMap(() "=> ajax('url""...')), take(3) ) .subscribe(res "=> { "// do something change view })
  27. Make a HTTP request Listen an events
 
 Read a

    file A set of values A set of values A set of values
  28. A set of values A set of values A set

    of values Observable Make a HTTP request Listen an events
 
 Read a file
  29. mouseClick$.pipe( switchMap(() "=> request$.pipe(takeUntil(cancel$)) ); ); .subscribe((value) "=> { "//

    do something }) mouseDown$.pipe( switchMap(() "=> mouseMove$.pipe(takeUntil(mouseUp$)) ) ) .subscribe(value "=> { "// do something }); 取消請求 Cancel Request 拖拉 D&D
  30. const postUser$ = r.pipe( r.matchPath('/user'), r.matchType('POST'), r.useEffect(req$ "=> req$.pipe( map(req

    "=> req.body as User), mergeMap(Dao.postUser), map(response "=> ({ body: response })) )));
  31. const pingEpic = action$ "=> action$.pipe( filter(action "=> action.type ""===

    'PING'), delay(1000), "// Asynchronously wait 1000ms then continue mapTo({ type: 'PONG' }) );