const button = document.getElementById('btn');
button.addEventListener('click', event => {/* ... */});
Callback
fetchInitialData((error, data) => {
if (!error) {
setTimeout(() => {
fetchData(data, (error, data) => {
if (error) {
handleError(error);
} else {
fetchOtherData(data, (error, data) => {
/* ... */
});
}
});
}, 1000);
}
});
fetchData(id)
.then(data => {
return fetchOtherData(data.id);
})
.then(data => {
return parseData(data);
})
.catch(error => {
handleError(error);
});
시간
"angular"
"react"
Ajax
Calls
UI
입력
❌
대부분의 비동기는 값이 여러개이다.
let observable: Observable;
observable.subscribe(observer);
const observer = {
next: value => console.log('next', value),
error: err => console.error('error', err),
complete: () => console.info('complete'),
};
Observable 구독
observable.subscribe(
value => console.log('next', vlaue),
err => console.error('error', err),
() => console.info('complete'),
);
const subscription = observable
.subscribe(observer);
Observable 구독 취소
subscription.unsubscribe();
Observable 생성
const ob = new Observable(subscriber => {
});
const ob = new Observable(subscriber => {
subscriber.next('0️⃣');
subscriber.next('1️⃣');
subscriber.next('2️⃣');
subscriber.complete();
});
ob.subscribe({
next: value => console.log('값: ${value}'),
complete: () => console.log('✅');
});
// 값: 0️⃣
// 값: 1️⃣
// 값: 2️⃣
// ✅
ob.subscribe({
});
Observable
오류 처리
const ob = new Observable(subscriber => {
});
const ob = new Observable(subscriber => {
subscriber.next('0️⃣');
subscriber.next('1️⃣');
subscriber.next('2️⃣');
subscriber.error('💀');
subscriber.next('값이');
subscriber.next('더 이상');
subscriber.next('흐르지 않아요.');
});
ob.subscribe({
next: value => console.log('값: ${value}'),
error: error => console.log(error),
complete: () => console.log('✅');
});
// 값: 0️⃣
// 값: 1️⃣
// 값: 2️⃣
// 💀
Observable은 오류 발생 이후에 종료됨
Observable
Cleanup
const ob = new Observable(subscriber => {
subscriber.next('0️⃣');
subscriber.next('1️⃣');
subscriber.next('2️⃣');
subscriber.complete();
});
const ob = new Observable(subscriber => {
subscriber.next('0️⃣');
subscriber.next('1️⃣');
subscriber.next('2️⃣');
subscriber.complete();
return () => {
console.log('Cleanup!');
};
});
const subscription = ob.subscribe();
subscription.unsubscribe();
// Cleanup!
Event Listener 해지 또는 Ajax abort 등
Observable.of
Observable.from
Observable.of('hello');
Observable.of(1, 2, 3);
Observable.from([1, 2, 3]);
Observable.from(otherObservable);
function transform(prev: Observable<number>): Observable<string>;
function transform(prev) {
return new Observable(subscriber => {
prev.subscribe(
value => subscriber.next(`값은 ${value} 입니다.`),
error => subscriber.error(error),
complete => subscriber.complete(),
);
});
}
transform(Observable.from([1, 2, 3]))
.subscribe(value => console.log(value));
// 값은 1 입니다.
// 값은 2 입니다.
// 값은 3 입니다.
Make
Chainable
Observable.prototype.map = mapFn => {
const source = this;
return new Observable(subscriber => {
source.subscribe(
value => subscriber.next(mapFn(value)),
error => subscriber.error(error),
() => subscriber.complete(),
);
});
};
Observable.from([1, 2, 3])
.map(x => x * 2)
.subscribe(value => console.log(value));
// 2
// 4
// 6
let clicks = 0;
let timeoutId;
function handleClick() {
clicks += 1;
if (!timeoutId) {
timeoutId = setTimeout(() => {
timeoutId = null;
clicks = 0;
}, 400);
return;
}
clearTimeout(timeoutId);
if (clicks <= 2) {
timeoutId = setTimeout(() => {
timeoutId = null;
clicks = 0;
console.log('더블클릭!');
}, 400);
} else {
timeoutId = setTimeout(() => {
timeoutId = null;
clicks = 0;
}, 400);
}
}
button
.addEventListener('click', handleClick);
const clicks = fromEvent(button, 'click');
clicks
.buffer(clicks.throttleTime(400))
.map(events => events.length)
.filter(count => count === 2)
.subscribe(() => {
console.log('더블클릭!');
});
비동기 흐름을 선언적으로 작성
(tc39 proposal stage1)
import { of, from, interval, ajax, fromEvent } from 'rxjs';
of(1, 2, 3)
from(Promise.resolve('future'));
interval(1000);
ajax('https://api.example.com');
fromEvent(button, 'click');
...
Observable.from([1, 2, 3])
.map(x => x * 2)
.filter(x => x < 5)
.subscribe();
Observable.from([1, 2, 3]).pipe(
map(x => x * 2),
filter(x => x < 5)
).subscribe();
rxjs@<5.5.0
rxjs@>=5.5.0
feat. Auto-Complete
RxJS로 짜보자!
(feat. Marble diagrams)
정의 | 기호 |
---|
= next
= error
= complete
시간 축 |
|
---|
이벤트 |
|
---|
Observable |
|
---|
❌
❌
⌨️
⌨️
⌨️
⌨️
"a"
"b"
"c"
"d"
Keyboard events
Input values
= 150ms
"a"
"a"
"b"
"c"
"d"
"d"
"e"
"e"
inner
outer
unsubscribe!
"a"
"b"
"c"
🍎
🍎
🥦
🥦
❌
❌
inner
outer
inner에서 에러가 발생하면
outer도 죽어버림
"a"
"b"
🍎
🍎
❌
switchMap
catchError
outer.pipe(
switchMap(x => inner(x)),
catchError(/* ... */)
).subscribe();
outer.pipe(
switchMap(x => inner(x).pipe(
catchError(/* ... */),
)),
).subscribe();
⌨︎
⌨︎
⌨︎
⌨︎
⌨︎
⌨︎
"a"
"b"
"c"
"d"
"e"
"f"
"b"
"c"
"d"
"f"
🍎
🥦
🍎
🥦
switchMap
debounceTime
map
Pull
Push
Program
Environment
Mouse Click
Keyboard
I/O
File
Pull
Push
Storage
Websockets
Observable이 좋은 선택이 될 수 있다.
More Things
function timeElapsed() {
const scheduler = animationFrameScheduler;
return defer(() => {
const start = scheduler.now();
return range(0, Number.POSITIVE_INFINITY, scheduler).pipe(
map(() => scheduler.now() - start)
);
});
}
// milliseconds 시간동안 0...1 까지의 값이 전달됨
function duration(milliseconds: number) {
return timeElapsed().pipe(
map(elapsedTime => elapsedTime / milliseconds),
takeWhile(timing => timing <= 1)
);
}
const startOffset = window.scrollY;
const distancePos = 1000;
const direction = distancePos >= startOffset ? 1 : -1;
const distance = Math.abs(distancePos - startOffset);
// 500ms 동안 1000px 위치로 ease-in-out 스크롤 시킴.
// 단, 중간에 사용자가 마우스휠을 하면 애니메이션은 취소됨.
duration(500).pipe(
map(easeInOut),
map(timing => timing * distance),
tap(frame => {
window.scrollTo(0, startOffset + frame * direction);
}),
takeUntil(fromEvent(window, 'mousewheel'))
).subscribe();
Stage1
function doubleSay (str) {
return str + ", " + str;
}
function capitalize (str) {
return str[0].toUpperCase() + str.substring(1);
}
function exclaim (str) {
return str + '!';
}
let result = exclaim(capitalize(doubleSay("hello")));
result //=> "Hello, hello!"
let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
result //=> "Hello, hello!"
const ob = Observable.from([1, 2, 3])
|> filter(x => x % 2 === 1)
|> map(x => x * 2);
ob.subscribe(val => console.log(val));
// 2
// 6
https://seokju.me