Reactive Extensions in JavaScript

An API for asynchronous programming
Don't we have that already?
Promises
Async / await
Box type
const Box = (x) =>
({
value: x,
map: (f) => Box(f(x)),
fold: (f) => f(x)
});
const identity = x => x;
const upper = (x) => x.toUpperCase();
const lower = (x) => x.toLowerCase();
const concat = (str) => (value) => value + str;
const vowels = /a|e|i|o|u/gi;
const replace = (pattern, v) => (s) => s.replace(pattern, v);
Operating on the box type
const myValue = Box("Belfast") // Box(Belfast)
.map(upper) // Box(BELFAST)
.map(concat(" JavaScript")) // Box(BELFAST JavaScript)
.map(replace(vowels, "")) // Box(BLFST Jvscrpt)
.map(lower) // Box(blfst jvscrpt)
console.log("box value:", myValue.fold(identity)); //box value: blfst jvscrpt
Imperative approach
Introduces state to keep track of control flow.
const inputWord = "Belfast";
const inputWordUpper = inputWord.toUpperCase();
const inputWordJs = inputWordUpper + " JavaScript";
const inputWordUpperNoVowels = inputWordUpper.replace(vowels, "");
const inputWordLower = inputWordUpperNoVowels.toLowerCase();
console.log("inputWordLower", inputWordLower);
Observable
An interface for dealing with async events.
(Lodash for async)
Observables from events
const node = document.querySelector("#yo");
const source = Rx.Observable
.fromEvent(node, "click")
.filter(x => x.clientY < 100)
.bufferCount(3)
.subscribe(console.log)
Observables are lazy
const source = Rx.Observable
.range(0, Infinity)
.filter(x => x % 2 === 0)
.take(5)
const subscribe = source.subscribe(val => console.log(val));
//0
//2
//4
//6
//8
fetchAttendees()
.then((attendees) =>
attendees.filter((attendee) => attendee.company === "Rapid7"))
.then((attendees) =>
attendess.map((attendee) => attendee.name))
.then((attendees) =>
attendees.forEach(console.log));
The pieces we care about are sitting behind then()
Promises
const attendees = await fetchAttendees()
const rapid7Attendees = attendees
.filter((attendee) => attendee.company === 'Rapid7'));
const rapid7AttendeeNames = rapid7Attendees.map((attendee) => attendee.name));
rapid7AttendeeNames.forEach(console.log);
We run into the same control flow problem we had before. We now have state.
Async / await
fetchAttendees()
.mergeAll()
.filter(propEq("company", "Rapid7"))
.map(prop("name"))
.subscribe(console.log);
Observable
const API_BASE_URL = "https://api.meetup.com";
const EVENT_NAME = "Belfast-JS";
const eventUrl = `${BASE_URL}/${EVENT_NAME}/events?key${API_KEY}&sign=true`;
const rsvps = (eventId) => `${BASE_URL}/${EVENT_NAME}/${eventId}/rsvps?key${API_KEY}&sign=true`;
const rsvps = Rx.Observable
.ajax(eventUrl)
.switchMap((events) =>
Rx.Observable.ajax(eventsUrl(events[0]))
.map((rsvps) =>
rsvps.member.photoUrl)
);
const rsvpPhotos = rsvps
.concatMap(Rx.Observable.ajax);
const delayRsvps = rsvps
.concatMap((url) =>
Rx.Observable.ajax(url).delay(3000));
Composing streams
const delayRsvps$ = rsvps
.concatMap((url) =>
Rx.Observable.ajax(url).delay(3000));
const bufferedClicks$ = Rx.Observable
.fromEvent(node, "click")
.filter(x => x.clientY < 100)
.bufferCount(3)
delayRsvps$
.takeUntil(bufferedClicks$)
.subscribe(console.log)
Cancellation api
What can promises do that an observable can't?
Nothing
Thanks
for listening
Reactive Extensions JS
By Daniel Skelton
Reactive Extensions JS
- 960