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.
RxJS
What’s RxJS ?
Lodash for async
Observable 

Library
Why we need RxJS ?
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(DOM) ...
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(DOM) ...
let isRequesting = false;
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM) > 0....
let isRequesting = false;
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM) > 0....
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM) > 0.9 #&& !isRequesting)
isReq...
Flag let isRequesting = false;
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM)...
let isRequesting = false;
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM) > 0....
let isRequesting = false;
scrollView.addEventListener('scroll', event !
const DOM = event.target;
if(hasScrolled(DOM) > 0....
const DOM = event.target;
if(hasScrolled(DOM) > 0.9 #&& !isRequesting)
isRequesting = true;
fetch('url%%...')
.then(res !=...
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(DOM) ...
What’s wrong ?
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(DOM) ...
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(DOM) ...
Flag
let isRequesting = false;
scrollView.addEventListener('scroll', event !=> {
const DOM = event.target;
if(hasScrolled(...
More Flags
let isRequesting = false;
let requestCount = 0;
const scrollHandler = function(event) {
const DOM = event.targe...
let isRequesting = false;
let requestCount = 0;
const scrollHandler = function(event) {
const DOM = event.target;
if(hasSc...
let isRequesting = false;
let requestCount = 0;
const scrollHandler = function(event) {
const DOM = event.target;
if(hasSc...
Jerry Hong
Front-End Engineer |
Website: blog.jerry-hong.com
Facebook: J.H.MingChen
ObservableObservable
What is Observable ?
What is Observable ?
Collection+ Time
Observable 

What is Observable ?
var mouseMove = Observable
.fromEvent(DOM, 'mousemove');
observable
var subscription = mouseMove
.subscribe(x !=> console....
Observable.of(2, 3, 4);
Observable.from([2, 3, 4]);
Observable.from(fetch('url'));
Observable.ajax('url');
Observable.from...
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 %%=== 0)
.subscribe({
next: x !=> console.log(...
observable
• Observable
•
• (subscribe)
• operators
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> ...
operator
• Observable
•
•
observable ( )
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 %%===...
observer
• observable
• next 

error complete
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 %%=== 0)
.subscribe({...
subscription
• observable
•
(unsubscribe)
•
var sub = Observable
.from([1, 2, 3])
.map(x !=> x + 1)
.filter(x !=> x % 2 %%...
Marble Diagram
--a--b--c--d--e|
Marble Diagram
- (10 frames)
n(0-9/a-z) (next)
| (complete)
# (error)
()
time
----0---1---2---3--
----0---1---2---3|
----0...
Observable.of(1, 2, 3); (123|)
Observable.interval(10) --012-01-0-0123-01234-01234..
Observable
.fromEvent(DOM, 'click') ---e--ee-e--e-...
Observable.interval(10)
.take(3)
.map(x !=> x + 1)
.filter(x !=> x % 2 %%=== 1)
-01234..
-012|
-1-(3|)
-01(2|)
-12(3|)
Observable.interval(20) --0-1-2-3-4-5-6-7..
-------e-----------
--0-1-2|
.takeUntil(
Observable
.fromEvent(DOM, 'click')
);
Observable
.fromEvent(DOM, 'click')
.map(() !=>
ajax('url%%...'))
.mergeAll();
-----e-e----
-----o-o----
 
 ----r|
----r|
...
Observable
.fromEvent(DOM, 'click')
.mergeMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
--------...
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
-------...
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
Observable
.fromEvent(DOM, 'click')
.switchMap(() !=>
ajax('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
--!
----------...
Observable
.fromEvent(DOM, 'click')
.exhaustMap(() !=>
fetch('url%%...'))
-----e-e----
-----o-o----
 
 ----r|
----r|
-----...
Observable.fromEvent(scrollView, 'scroll')
.map(event !=> event.target)
.map(hasScrolled)
.filter(p !=> p > 0.9)
.exhaustM...
Make Your Code Clean
Readable
Readable Composable
Readable TestableComposable
Readable
Observable.fromEvent(scrollView, 'scroll')
.map(event !=> event.target)
.map(hasScrolled)
.filter(p !=> p > 0.9)
.exhaustM...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scroll$
.map(event !=> event.target)
.map(hasScrolled)
.filte...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scroll$
.map(event !=> event.target)
.map(hasScrolled)
.filte...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
const scrollOverNinetyPercent = Obs !=>
Obs.map(event !=> eve...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
const scrollOverNinetyPercent = Obs !=>
Obs.map(event !=> eve...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
const scrollOverNinetyPercent = Obs !=>
Obs.map(event !=> eve...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scrollOverNinetyPercent
scroll$
.let(scrollOverNinetyPercent)...
import { scrollOverNinetyPercent } from '%%...';
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scroll$
.let...
import { scrollOverNinetyPercent } from '%%...';
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scroll$
.let...
import { scrollOverNinetyPercent } from '%%...';
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
const getPos...
import { scrollOverNinetyPercent } from '%%...';
import { getPostObservable } from 'xxx';
const scroll$ = Observable
.from...
const scroll$ = Observable
.fromEvent(scrollView, 'scroll');
scroll$
.let(scrollOverNinetyPercent)
.exhaustMap(getPostObse...
Composable
const scrollOverNinetyPercent = Obs !=>
Obs.map(event !=> event.target)
.map(hasScrolled)
.filter(p !=> p > 0.9);
const scrollOver = criticalP !=> Obs !=>
Obs.map(event !=> event.target)
.map(hasScrolled)
.filter(p !=> p > criticalP);
H...
const scrollOver = criticalP !=>
pipe(
map(event !=> event.target),
map(hasScroll),
filter(p !=> p > criticalP)
);
lettabl...
import { scrollOverNinetyPercent } from '%%...';
import { getPostObservable } from 'xxx';
const scroll$ = Observable
.from...
import { scrollOver } from '%%...';
import { getPostObservable } from 'xxx';
const scroll$ = Observable
.fromEvent(scrollV...
pipe
operator
import { scrollOver } from '%%...';
import { getPostObservable } from 'xxx';
const scroll$ = Observable
.fro...
const getPostObservable = () !=>
Observable.ajax('url%%...');
retry 3
const getPostObservable = () !=>
Observable.ajax('url%%...')
.retry(3);
const defaultData = { success: false, data: [] };
const getPostObservable = () !=>
Observable.ajax('url%%...')
.retry(3)
....
Array
const defaultData = { success: false, data: [] };
const getPostObservable = () !=>
Observable.ajax('url%%...')
.retr...
lettable operator
import { retry, catchError } from 'rxjs/operators';
const defaultData = { success: false, data: [] };
co...
lettable operator
import { retry, catchError } from 'rxjs/operators';
const defaultData = { success: false, data: [] };
co...
import { retry, catchError } from 'rxjs/operators';
onErrorReturn
const defaultData = { success: false, data: [] };
const ...
Observable
Observable is composable
(Drag&Drop)
https://dribbble.com/shots/1074817-Drag-Drop-List-GIF
mouseDown$
mouseDown$
.switchMap(() !=> mouseMove$)
mouseDown$
mouseDown$
.switchMap(() !=> mouseMove$)
mouseDown$
.switchMap(() !=>
mouseMove$.takeUntil(mouseUp$))
mouseDown$
.switchMap(() !=>
mouseMove$.takeUntil(mouseUp$))
.subscribe(value !=> {
#// do something
});
mouseClick$
mouseClick$
.switchMap(() !=> request$)
mouseClick$
mouseClick$
.switchMap(() !=> request$)
mouseClick$
.switchMap(() !=>
request$.takeUntil(cancel$))
mouseClick$
.switchMap(() !=>
request$.takeUntil(cancel$))
.subscribe(value !=> {
#// do something
});
mouseClick$
.switchMap(() !=>
request$.takeUntil(cancel$))
.subscribe(value !=> {
#// do something
});
mouseClick$
.switchMap(() !=>
request$.takeUntil(cancel$))
.subscribe(value !=> {
#// do something
});
mouseDown$
.switchM...
Testable
Testing Asynchronous Code is
Hard
Testing Asynchronous Code is Hard
•
•
•
•
Testing Asynchronous Code is Hard
•
•
•
•
•
Marble Testing
Marble Testing
• Marble Diagram
•
• 100%
• RxJS
•
Observable.interval(10)
.take(3)
.map(x !=> x + 1)
.filter(x !=> x % 2 %%=== 1)
-01234..
-012|
-1-(3|)
-01(2|)
-12(3|)
it('interval', () !=> {
const actual = Observable.interval(10, testScheduler)
.take(3)
.map(x !=> x + 1)
.filter(x !=> x %...
https://youtu.be/i2A1S9o7ZFQ
Programming is thinking, not typing
– Cassey Pottan
Be a Programmer, not just a Coder
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
RxJS - 封裝程式的藝術
Upcoming SlideShare
Loading in …5
×

RxJS - 封裝程式的藝術

5,172 views

Published on

這幾年來 JavaScript 有越來越多的語法糖(syntax sugar)像是 async/await, generator 等等,但我們實際上在處理非同步行為時,仍然要透過各種不同的方式;這使我們必須學習越來越多的語法,但程式碼卻更加難以閱讀。本次演講將會說明 RxJS 如何使用相同的方式處理各種非同步行為,以及我們要如何運用 Functional Programming 的觀念把複雜的非同步行為封裝成簡單可讀的程式碼。

Published in: Technology
  • Sex in your area is here: www.bit.ly/sexinarea
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Dating for everyone is here: www.bit.ly/2AJerkH
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Sex in your area for one night is there tinyurl.com/hotsexinarea Copy and paste link in your browser to visit a site)
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Girls for sex are waiting for you https://bit.ly/2TQ8UAY
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Meetings for sex in your area are there: https://bit.ly/2TQ8UAY
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

RxJS - 封裝程式的藝術

  1. 1. RxJS
  2. 2. What’s RxJS ?
  3. 3. Lodash for async
  4. 4. Observable Library
  5. 5. Why we need RxJS ?
  6. 6. 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; }); } });
  7. 7. 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; }); } });
  8. 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;
  9. 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; });
  10. 10. 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
  11. 11. Flag let isRequesting = false; scrollView.addEventListener('scroll', event ! const DOM = event.target; if(hasScrolled(DOM) > 0.9 #&& !isRequesting) isRequesting = true; fetch('url%%...') .then(res !=> {
  12. 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; }); isRequesting false 
 Request
  13. 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; }); } Request isRequest true
  14. 14. 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
  15. 15. 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; }); } });
  16. 16. What’s wrong ?
  17. 17. 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; }); } });
  18. 18. 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
  19. 19. 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; }); } });
  20. 20. 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);
  21. 21. 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); 

  22. 22. 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); Observable.fromEvent(scrollView, 'scroll') .map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9) .exhaustMap(() !=> fetch('url%%...')) .take(3) .subscribe(res !=> { #// do something change view }); VanillaJS RxJS
  23. 23. Jerry Hong Front-End Engineer | Website: blog.jerry-hong.com Facebook: J.H.MingChen
  24. 24. ObservableObservable
  25. 25. What is Observable ?
  26. 26. What is Observable ? Collection+ Time
  27. 27. Observable 
 What is Observable ?
  28. 28. var mouseMove = Observable .fromEvent(DOM, 'mousemove'); observable var subscription = mouseMove .subscribe(x !=> console.log(x)); subscription.unsubscribe();
  29. 29. Observable.of(2, 3, 4); Observable.from([2, 3, 4]); Observable.from(fetch('url')); Observable.ajax('url'); Observable.fromEvent(DOM, 'click'); Observable.interval(1000); observable
  30. 30. var sub = Observable .from([1, 2, 3]) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 0) .subscribe({ next: x !=> console.log(x), error: err !=> {}, complete: () !=> {}, });
  31. 31. observable • Observable • • (subscribe) • operators var sub = Observable .from([1, 2, 3]) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 0) .subscribe({ next: x !=> console.log(x), error: err !=> {}, complete: () !=> {}, });
  32. 32. operator • Observable • • observable ( ) var sub = Observable .from([1, 2, 3]) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 0) .subscribe({ next: x !=> console.log(x), error: err !=> {}, complete: () !=> {}, });
  33. 33. observer • observable • next 
 error complete .from([1, 2, 3]) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 0) .subscribe({ next: x !=> console.log(x), error: err !=> {}, complete: () !=> {}, });
  34. 34. subscription • observable • (unsubscribe) • var sub = Observable .from([1, 2, 3]) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 0) .subscribe({ next: x !=> console.log(x), error: err !=> {}, complete: () !=> {}, });
  35. 35. Marble Diagram --a--b--c--d--e|
  36. 36. 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|)
  37. 37. Observable.of(1, 2, 3); (123|)
  38. 38. Observable.interval(10) --012-01-0-0123-01234-01234..
  39. 39. Observable .fromEvent(DOM, 'click') ---e--ee-e--e-...
  40. 40. Observable.interval(10) .take(3) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 1) -01234.. -012| -1-(3|) -01(2|) -12(3|)
  41. 41. Observable.interval(20) --0-1-2-3-4-5-6-7.. -------e----------- --0-1-2| .takeUntil( Observable .fromEvent(DOM, 'click') );
  42. 42. Observable .fromEvent(DOM, 'click') .map(() !=> ajax('url%%...')) .mergeAll(); -----e-e---- -----o-o---- ----r| ----r| ---------r-r-
  43. 43. Observable .fromEvent(DOM, 'click') .mergeMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r| ---------r-r-
  44. 44. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r| -----------r-
  45. 45. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  46. 46. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  47. 47. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  48. 48. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  49. 49. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  50. 50. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  51. 51. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  52. 52. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  53. 53. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| ----r|
  54. 54. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --!
  55. 55. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --!
  56. 56. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --!
  57. 57. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --!
  58. 58. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --!
  59. 59. Observable .fromEvent(DOM, 'click') .switchMap(() !=> ajax('url%%...')) -----e-e---- -----o-o---- ----r| --! -----------r-
  60. 60. Observable .fromEvent(DOM, 'click') .exhaustMap(() !=> fetch('url%%...')) -----e-e---- -----o-o---- ----r| ----r| ---------r---
  61. 61. Observable.fromEvent(scrollView, 'scroll') .map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9) .exhaustMap(() !=> fetch('url%%...')) .take(3) .subscribe(res !=> { #// do something change view });
  62. 62. Make Your Code Clean
  63. 63. Readable
  64. 64. Readable Composable
  65. 65. Readable TestableComposable
  66. 66. Readable
  67. 67. Observable.fromEvent(scrollView, 'scroll') .map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); 
 Observable
  68. 68. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); Assign
  69. 69. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); 
 Operator
  70. 70. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); const scrollOverNinetyPercent = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9); scrollOverNinetyPercent(scroll$) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); Naming Function
  71. 71. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); const scrollOverNinetyPercent = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); let operator
  72. 72. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); const scrollOverNinetyPercent = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); let operator
  73. 73. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scrollOverNinetyPercent scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); const scroll$ = Observable .fromEvent(scrollView, 'scroll'); const scrollOverNinetyPercent = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); let operator
  74. 74. import { scrollOverNinetyPercent } from '%%...'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view });
  75. 75. import { scrollOverNinetyPercent } from '%%...'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(() !=> fetch('url%%...')) .subscribe(res !=> { #// do something change view }); observable creator
  76. 76. import { scrollOverNinetyPercent } from '%%...'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); const getPostObservable = () !=> fetch('url%%...');
 scroll$ .let(scrollOverNinetyPercent) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view }); import { scrollOverNinetyPercent } from '%%...'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); getPostObservable
 scroll$ .let(scrollOverNinetyPercent) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view });
  77. 77. import { scrollOverNinetyPercent } from '%%...'; import { getPostObservable } from 'xxx'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view });
  78. 78. const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view });
  79. 79. Composable
  80. 80. const scrollOverNinetyPercent = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > 0.9);
  81. 81. const scrollOver = criticalP !=> Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > criticalP); Higher Order Function const scrollOver = Obs !=> Obs.map(event !=> event.target) .map(hasScrolled) .filter(p !=> p > );
  82. 82. const scrollOver = criticalP !=> pipe( map(event !=> event.target), map(hasScroll), filter(p !=> p > criticalP) ); lettable operator import { map, filter } from 'rxjs/operators';
  83. 83. import { scrollOverNinetyPercent } from '%%...'; import { getPostObservable } from 'xxx'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOverNinetyPercent) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view });
  84. 84. import { scrollOver } from '%%...'; import { getPostObservable } from 'xxx'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .let(scrollOver(0.9)) .exhaustMap(getPostObservable) .subscribe(res !=> { #// do something change view });
  85. 85. pipe operator import { scrollOver } from '%%...'; import { getPostObservable } from 'xxx'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .pipe( scrollOver(0.9), exhaustMap(getPostObservable) ) .subscribe(res !=> { #// do something change view }); import { scrollOver } from '%%...'; import { getPostObservable } from 'xxx'; import { exhaustMap } from 'rxjs/operators'; const scroll$ = Observable .fromEvent(scrollView, 'scroll'); scroll$ .pipe( scrollOver(0.9), exhaustMap(getPostObservable) ) .subscribe(res !=> { #// do something change view });
  86. 86. const getPostObservable = () !=> Observable.ajax('url%%...');
  87. 87. retry 3 const getPostObservable = () !=> Observable.ajax('url%%...') .retry(3);
  88. 88. const defaultData = { success: false, data: [] }; const getPostObservable = () !=> Observable.ajax('url%%...') .retry(3) .catch(() !=> Observable.of(defaultData));
  89. 89. Array const defaultData = { success: false, data: [] }; const getPostObservable = () !=> Observable.ajax('url%%...') .retry(3) .catch(() !=> [defaultData]);
  90. 90. lettable operator import { retry, catchError } from 'rxjs/operators'; const defaultData = { success: false, data: [] }; const getPostObservable = () !=> Observable.ajax('url%%...') .pipe( retry(3), catchError(() !=> [defaultData]) );
  91. 91. lettable operator import { retry, catchError } from 'rxjs/operators'; const defaultData = { success: false, data: [] }; const onErrorReturn = defaultData !=> catchError(() !=> [defaultData]); const getPostObservable = () !=> Observable.ajax('url%%...') .pipe( retry(3), onErrorReturn(defaultData) );
  92. 92. import { retry, catchError } from 'rxjs/operators'; onErrorReturn const defaultData = { success: false, data: [] }; const getPostObservable = () !=> Observable.ajax('url%%...') .pipe( retry(3), onErrorReturn(defaultData) ); import { onErrorReturn } from '%%...';
  93. 93. Observable
  94. 94. Observable is composable
  95. 95. (Drag&Drop) https://dribbble.com/shots/1074817-Drag-Drop-List-GIF
  96. 96. mouseDown$
  97. 97. mouseDown$ .switchMap(() !=> mouseMove$) mouseDown$
  98. 98. mouseDown$ .switchMap(() !=> mouseMove$)
  99. 99. mouseDown$ .switchMap(() !=> mouseMove$.takeUntil(mouseUp$))
  100. 100. mouseDown$ .switchMap(() !=> mouseMove$.takeUntil(mouseUp$)) .subscribe(value !=> { #// do something });
  101. 101. mouseClick$
  102. 102. mouseClick$ .switchMap(() !=> request$) mouseClick$
  103. 103. mouseClick$ .switchMap(() !=> request$)
  104. 104. mouseClick$ .switchMap(() !=> request$.takeUntil(cancel$))
  105. 105. mouseClick$ .switchMap(() !=> request$.takeUntil(cancel$)) .subscribe(value !=> { #// do something });
  106. 106. mouseClick$ .switchMap(() !=> request$.takeUntil(cancel$)) .subscribe(value !=> { #// do something });
  107. 107. mouseClick$ .switchMap(() !=> request$.takeUntil(cancel$)) .subscribe(value !=> { #// do something }); mouseDown$ .switchMap(() !=> mouseMove$.takeUntil(mouseUp$)) .subscribe(value !=> { #// do something });
  108. 108. Testable
  109. 109. Testing Asynchronous Code is Hard
  110. 110. Testing Asynchronous Code is Hard • • • •
  111. 111. Testing Asynchronous Code is Hard • • • • •
  112. 112. Marble Testing
  113. 113. Marble Testing • Marble Diagram • • 100% • RxJS •
  114. 114. Observable.interval(10) .take(3) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 1) -01234.. -012| -1-(3|) -01(2|) -12(3|)
  115. 115. it('interval', () !=> { const actual = Observable.interval(10, testScheduler) .take(3) .map(x !=> x + 1) .filter(x !=> x % 2 %%=== 1); testScheduler.expectObservable(actual) .toBe('-a-(b|)', { a: 1, b: 3 }); });
  116. 116. https://youtu.be/i2A1S9o7ZFQ
  117. 117. Programming is thinking, not typing – Cassey Pottan
  118. 118. Be a Programmer, not just a Coder

×