Łukasz Karpuć, Marzec/Kwiecień 2019
onClick() {
makeSquareRed();
makeCirleRed();
}
onMouseOver() {
makeSquareGreen();
makeCircleGreen();
}
onKeypress() {
makeSquareBlue();
makeCirleBlue();
}
onClick() {
makeSquareRed();
makeCirleRed();
makeTriangleRed();
}
onMouseOver() {
makeSquareGreen();
makeCircleBlue();
makeTriangleGreen();
}
onKeypress() {
makeSquareBlue();
makeCirleBlue();
makeTriangleBlue();
}
onClick() {
color = 'red'
}
onMouseOver() {
color = 'green'
}
onKeypress() {
color = 'blue'
}
setInterval(() => renderAll(), 1000)
onClick() {
color = 'red'
}
onMouseOver() {
color = 'green'
}
onKeypress() {
color = 'blue'
}
observeColor(() => renderAll())
SABRE
PROGRAMISTA
$$$$$$$
PUSH
PROGRAMISTA
PULL
URZĄD
SKARBOWY
$$$
*żródło: https://sourcemaking.com
*żródło: https://sourcemaking.com
interface Observer<T> {
closed?: boolean;
next: (value: T) => void;
error: (err: any) => void;
complete: () => void;
}
interface Subscribable<T> {
subscribe(observer: Observer<T>): Subscription;
}
interface Observable<T> extends Subscribable<T> {
new (observer: Observer<T>): this;
}
interface Subject<T, R> extends Observer<T>, Subscribable<R> {
}
interface Observer<T> {
// REACTS to data changes
}
interface Subscribable<T> {
// is a SOURCE of data
}
interface Observable<T> extends Subscribable<T> {
// DELIVERS data; is mostly stateless
}
interface Subject<T, R> extends Observer<T>, Subscribable<R> {
// PROPAGATES data; is mostly stateful
}
Jest źródłem danych.
Nie jest odbiorcą danych.
Nie posiada stanu.
Każda subskrypcja powoduje ponowne przetworzenie kolekcji.
Jest źródłem danych.
Jest odbiorcą danych.
Posiada stan.
Subskrypcje są z nim powiązane. Nie muszą otrzymywać tych samych danych.
// Observable example
const observable = new Observable(subscriber => {
subscriber.next(1);
subscriber.next(2);
subscriber.next(3);
setTimeout(() => {
subscriber.next(4);
subscriber.complete();
}, 1000);
});
console.log('just before subscribe');
observable.subscribe({
next(x) { console.log('got value ' + x); },
error(err) { console.error('something wrong occurred: ' + err); },
complete() { console.log('done'); }
});
console.log('just after subscribe');
// just before subscribe
// got value 1
// got value 2
// got value 3
// just after subscribe
// got value 4
// done
// Subject example
const subject = new ReplaySubject(3); // buffer 3 values for new subscribers
subject.subscribe({
next: (v) => console.log(`observerA: ${v}`)
});
subject.next(1);
subject.next(2);
subject.next(3);
subject.next(4);
subject.subscribe({
next: (v) => console.log(`observerB: ${v}`)
});
subject.next(5);
// Logs:
// observerA: 1
// observerA: 2
// observerA: 3
// observerA: 4
// observerB: 2
// observerB: 3
// observerB: 4
// observerA: 5
// observerB: 5
// map operator example
//emit ({name: 'Joe', age: 30}, {name: 'Frank', age: 20},{name: 'Ryan', age: 50})
const source = from([
{ name: 'Joe', age: 30 },
{ name: 'Frank', age: 20 },
{ name: 'Ryan', age: 50 }
]);
//grab each persons name, could also use pluck for this scenario
const example = source.pipe(
map(({ name }) => name)
);
//output: "Joe","Frank","Ryan"
const subscribe = example.subscribe(val => console.log(val));
// filter example
//emit every second
const source = interval(1000);
//filter out all values until interval is greater than 5
const example = source.pipe(
filter(num => num > 5)
);
/*
"Number greater than 5: 6"
"Number greater than 5: 7"
"Number greater than 5: 8"
"Number greater than 5: 9"
*/
const subscribe = example.subscribe(val =>
console.log(`Number greater than 5: ${val}`)
);
// scan example (continuous reduce)
const subject = new Subject();
//scan example building an object over time
const example = subject.pipe(
scan((acc, curr) => Object.assign({}, acc, curr), {})
);
//log accumulated values
const subscribe = example.subscribe(val => console.log('Accumulated object:', val));
//next values into subject, adding properties to object
// {name: 'Joe'}
subject.next({ name: 'Joe' });
// {name: 'Joe', age: 30}
subject.next({ age: 30 });
// {name: 'Joe', age: 30, favoriteLanguage: 'JavaScript'}
subject.next({ favoriteLanguage: 'JavaScript' });
// custom operator example
let store = sidepanelService.getStore(); // Redux Store
let store$ = Rx.Observable.from(store); // Store's state update
let mutations$ = store$
.pipe(observeMutation(KeyValueMutation.calculateMutation)); // Store's state mutation
mutations$
.pipe(filter(it => it.mutatedKeys.visibleItem)) // Mutation of given attribute
.subscribe(it => this.getSidepanelWidget()._setVisibleItemKey(it.new.visibleItem));
mutations$
.pipe(filter(it => mutation.mutatedKeys.isWideMode)) // Mutation of given attribute
.subscribe(it => this.getSidepanelWidget()._handleWideModeChange(it.new.isWideMode));
pull | push | |
---|---|---|
skalar | function() | Promise |
kolekcja | function*() |
pull | push | |
---|---|---|
skalar | function() | Promise |
kolekcja | function*() | bservable |
- Czemu Pan ukradł to piwo?
- Herr Wysoki Sądzie, ja nic nie ukradłem... Pisało "bier", to wziąłem!
Dobre katalogi operatorów na:
it('test with jest only', (done) => {
// given
let source = from([1, 2, 3, 4]);
let expected = [2, 4, 6, 8];
// when
let result = source.pipe(map(it => it * 2));
result.subscribe(function(value) {
// then
expect(value).toEqual(expected.shift());
if( !expected.length ) {
done();
}
});
});
it('test with rxjs-marbles', marbles(m => {
let numbers = [...new Array(10).keys()];
// given
let source = m.hot( '-0-1-2-3-|', numbers);
let subs = '^--------!';
let expected = m.cold( '-0-2-4-6-|', numbers);
// when
let result = source.pipe(map(it => it * 2));
// then
m.expect(result).toBeObservable(expected);
m.expect(source).toHaveSubscriptions(subs);
}));
it('test with jasmine-marbles', () => {
let numbers = [...new Array(10).keys()];
// given
let source = jm.hot( '-0-1-2-3-|', numbers);
let subs = '^--------!';
let expected = jm.cold( '-0-2-4-6-|', numbers);
// when
let result = source.pipe(map(it => it * 2));
// then
expect(result).toBeObservable(expected);
expect(source).toHaveSubscriptions(subs);
});
it('test with jasmine-marbles', () => {
let numbers = [...new Array(10).keys()];
// given
let source = jm.hot( '-0-1-2-3-|', numbers);
let expected = jm.cold( '-0-2-4-6-|', numbers);
// when
let result = source.pipe(map(it => it * 2));
// then
expect(result).toBeObservable(expected);
});
it('test with rxjs/testing', () => {
let scheduler = new TestScheduler((actual, expected) =>
expect(actual).toEqual(expected));
scheduler.run(m => {
let numbers = [...new Array(10).keys()];
// given
let source = m.hot( '-0-1-2-3-|', numbers);
let subs = '^--------!';
let expected = '-0-2-4-6-|';
// when
let result = source.pipe(map(it => it * 2));
// then
m.expectObservable(result, subs).toBe(expected, numbers);
m.expectSubscriptions(result.subscriptions).toBe(subs);
});
});
it('test with rxjs/testing', () => {
let scheduler = new TestScheduler((actual, expected) =>
expect(actual).toEqual(expected));
scheduler.run(m => {
let numbers = [...new Array(10).keys()];
// given
let source = m.hot( '-0-1-2-3-|', numbers);
let subs = '^--------!';
let expected = '-0-2-4-6-|';
// when
let result = source.pipe(map(it => it * 2));
// then
m.expectObservable(result, subs).toBe(expected, numbers);
m.expectSubscriptions(result.subscriptions).toBe(subs);
});
});
abo Marble Syntax
-1---2-----3-4---5-|
---A--B--CD--------|
---X-YZ--STR-W---U-|
{ X: '1A', Y: '2A', Z: '2B', /* ... */ U: '5D' }
-
----------
Wirtualna ramka czasu
Hot Observable / Cold Observable / Subskrypcje
[0-9]+[ms|s|m]
- 8ms -
Upływ czasu
Hot Observable / Cold Observable / Subskrypcje
|
- 8ms -|
Koniec kolekcji (.complete())
Hot Observable / Cold Observable / Subskrypcje
#
- 8ms -#
Błąd (.error())
Hot Observable / Cold Observable / Subskrypcje
[a-z0-9]
- 8ms -a-b-c-|
Kolejna wartość (.next())
Hot Observable / Cold Observable / Subskrypcje
()
- 8ms -a-b-(c|)
Zdarzenia równoległe
Hot Observable / Cold Observable / Subskrypcje
^
^ 8ms ----------
- 8ms -a^b-(c|)
Początek subskrypcji (.subscribe())
Hot Observable / Cold Observable / Subskrypcje
!
^ 8ms --------!
Koniec subskrypcji (.unsubscribe())
Hot Observable / Cold Observable / Subskrypcje
const source = hot('--a--a--a--a--a--a--a--');
const sub1 = ' --^-----------!';
const sub2 = ' ---------^--------!';
const expect1 = ' --a--a--a--a--';
const expect2 = ' -----------a--a--a-';
expectObservable(source, sub1).toBe(expect1);
expectObservable(source, sub2).toBe(expect2);
Przykład z dokumentacji
Nie jest bezpośrednim źródłem danych.
Producent najpewniej żyje już gdzieś w systemie.
Dlatego dla tego rodzaju observable'a można zaznaczyć moment rozpoczęcia subskrypcji.
Jest bezpośrednim źródłem danych.
Tworzy producenta (w zasadzie sam observable nim jest).