Reactive programming and RxJS
Product
Vendor
Cart
Cupon
Sale
Payment
User profile
Invoice
Cart
Invoice
Add product...
...update the total
Cart
Invoice
import {Invoice} from './invoice';
class Cart {
constructor(invoice: Invoice){ ... }
addProduct(product) {
...
this.invoice.update(product); // needs to be public
...
}
}
Passive
Proactive
Cart
Invoice
import {Cart} from './cart';
class Invoice {
constructor(cart: Cart){ ... }
init() {
...
this.cart.onCartChange( callback )
}
}
Reactive
Listenable
Cart
Cupon
Sale
Payment
Invoice
Passive programming
Cart
Cupon
Sale
Payment
Invoice
Reactive programming
// concert spectators
let inMainStand = 300;
let inVipStand = 50;
// code...
let total = inMainStand + inVipStand; // 350
// code...
inMainStand += 60;
console.log(total); // total = ?
total = inMainStand + inVipStand; // 410
Interruptions in web applications
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
SYNC
function add(a, b) {
return a + b;
}
add(1,2); // imediately returns 3
add(2,3); // imediately returns 4
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
generators
function *generatorFunction() {
yield 'Hello, ';
yield 'World!';
}
const generatorObject = generatorFunction();
generatorObject.next(); // {value: "Hello, ", done: false}
generatorObject.next(); // {value: "World!", done: false}
generatorObject.next(); // {value: undefined, done: true}
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
promises
generators
const promise = new Promise((resolve, reject) => {
someAsyncFunction(err, data) {
if(err) return reject(err);
resolve(data)
}
});
promise
.then(value => console.log(value))
.catch(error => console.log(err))
Managing side-effects
SYNC
ASYNC
SINGLE
MULTIPLE
function
promises
generators
observables
PULL
PUSH
Observer pattern
Iterator pattern
getData(
function(successResult) {
// do something with the data
},
function(faliureError) {
// handle the error
},
);
result
.then(...)
.then(...)
.then(...)
let result = fetch('api/users.json');
result
.then(success => {
// handle success
})
.catch(error => {
// handle error
})
function asyncTask(i) {
return new Promise(resolve => resolve(i + 1));
}
async function runAsyncTasks() {
const res1 = await asyncTask(0);
const res2 = await asyncTask(res1);
const res3 = await asyncTask(res2);
return "Everything done"
}
runAsyncTasks().then(result => console.log(result));
Unifying callbacks, promises and event handlers
Shape of an observable:
Excersises: bit.ly/bjs-rxjs-ex1
let observable$ = new Observable(() => {
})
Observable
let observable$ = new Observable(observer => {
observer.next(1);
observer.next(2);
})
Observer
let observable$ = new Observable(observer => {
observer.next(1);
observer.next(2);
})
observable$.subscribe(value => {
console.log(value)
}
Subscription
let observable$ = new Observable(observer => {
observer.next(1);
observer.next(2);
return () => {
// cleanup resources when done
};
})
const subscription = observable$.subscribe(value => {
console.log(value)
})
subscription.unsubscribe(); // stop emitting values
let observable$ = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.error(new Error('Bad'));
})
const subscription = observable$.subscribe(
value => { console.log(value)},
error => { console.log(error.message)}
)
Creation functions
of(value1, value2, value3)
from(promise/itterable/observable)
fromEvent(target, eventName)
interval(time)
timer(time)
// COLD
var cold = new Observable((observer) => {
var producer = new Producer();
// have observer listen to producer here
});
// HOT
var producer = new Producer();
var cold = new Observable((observer) => {
// have observer listen to producer here
});
Observables are unicast - each subscriber manages its own execution context
Subjects are multicast observables - values are multicasted to many Observers
Types of subjects
Observables are collections of pushed values or events that we can:
let array = [1,2,3,4,5];
array
.map(v => v * v)
.filter(v => v > 5 && v < 20)
.reduce((acc, curr) => acc + curr, 0)
// 25
let observable$ = from([1,2,3,4,5]);
observable$
.pipe(
map(v => v * v),
filter(v => v > 5 && v < 20),
reduce((acc, curr) => acc + curr, 0)
)
.subscribe(val => console.log(val))
// 25
const buttonObs$ = fromEvent(querySelector('button'), 'click');
buttonObs$.subscribe(() => {
http$.get('/api/users').subscribe( data => {
// handle loaded data
})
})
const obs$ = new Observable(observer => {
observer.closed; // false
observer.next(1); // emit 1
observer.complete();
observer.closed; // true
observer.next(2); // won't emit
});
const obs$ = new Observable(observer => {
observer.closed; // false
observer.next(1); // emit 1
observer.error(new Error('Bad!'));
observer.closed; // true
observer.next(2); // won't emit
});
obs$
.subscribe({
next: v => console.log(v),
error: e => console.log(e.message)
});
const obs$ = new Observable(observer => {
observer.next(1);
observer.error(new Error('BANG!'));
}).pipe(
catchError(err => {
console.log('Intercepted error:' + err);
return of('I got this');
})
)
obs$.subscribe(v => console.log(v));
// 1 'I got this'
const getData$ = http.get('/api/users')
.pipe(
retry(3),
catchError(() => of('Something went wrong');
)
getData$.subscribe(value => console.log(value));
const getData$ = http.get('/api/users')
.pipe(
retry(3),
catchError(() => of('Something went wrong');
)
getData$.subscribe(value => console.log(value));