Observables and RxJS

What is Observable?

Opposite for Iterator)

function* iterator(){
    let index = 0;
        while(true)
            yield index++;
}

for (let i of iterator()) {
    if (i < 10) {
        console.log(i);
    } else {
        break;
    }
}

console.log("finished");
(async function () {
    function* iterator(){
        let index = 0;
        
        while(true) {
            yield new Promise((r) => { 
                setTimeout(() => { r(index++) }, 1000) 
            });
        }
    }
    
    for await (let i of iterator()) {
        if (i < 10) {
            console.log(i);
        } else {
            break;
        }
    }
    
    console.log("finished");
})();

Observer

getData().subscribe((data) => {
    render(data);
});

Why we need Observable?

We have Promises and Thunks

function httpGet(url) {

  return new Promise(function(resolve, reject) {

    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);

    xhr.onload = function() {
      if (this.status == 200) {
        resolve(this.response);
      } else {
        var error = new Error(this.statusText);
        error.code = this.status;
        reject(error);
      }
    };

    xhr.onerror = function() {
      reject(new Error("Network Error"));
    };

    xhr.send();
  });

}
Promise.race([
    new Promise((r) => { 
        setTimeout(() => { r("hello") }, 2000); 
    }), 
    new Promise((_, r) => { 
        setTimeout(() => { r(new Error("Timeout")) }, 1000) 
    })
])
.then(a => console.log(a), a => console.warn(a))

We have EventEmitter

var events = require('events');
var eventEmitter = new events.EventEmitter();

var listner1 = () => console.log('listner1 executed.');
var listner2 = () => console.log('listner2 executed.');

eventEmitter.addListener('connection', listner1);
eventEmitter.on('connection', listner2);

var eventListeners = events.EventEmitter.listenerCount(eventEmitter,'connection');
console.log(eventListeners + " Listner(s) listening to connection event");

eventEmitter.emit('connection');

eventEmitter.removeListener('connection', listner1);

eventEmitter.emit('connection');

console.log("Program Ended.");

Why we need Observables?

Observables

  • Common protocol
  • Lazy computations
  • Flow Control

Common protocol

Promise

 

function Producer () {
    return new Promise (r => {
        let data = 24;

        setTimeout(() => {
          r({ 
            data, 
            next: new Promise((r2) => {
                setTimeout(() => {
                    r2({ data: 18, next: undefined });
                }, 1000);
            }) 
          });
        }, 1000);
    });
}

var result = 0;
Producer().then(function Consumer ({ data, next }) {
    // do something with data;
    result += data;
    if (next) {
        return next.then(Consumer);
    }

    return `finished: ${ result }`;
}).then(a => console.log(a));

EventEmitter

var events = require("events");

function Producer () {
    let target = new events.EventEmmiter();
    let data = 24;

    setTimeout(() => {
        target.emit("progress", data);

        setTimeout(() => {
            target.emit("progress", 18);
            target.emit("end", undefined);
        }, 1000);
    }, 1000);

    // target.emit("error", new Error("some error that should stop all processes"));

    return target;
}

Observable

new Observable(observer => {
    let handler = event => observer.next(event);

    element.addEventListener("mousemove", handler, true);

    setTimeout(() => {
        observer.complete();
    }, 1000);

    // observer.error("some error");

    return () => {
        element.removeEventListener(eventName, handler, true);
    };
});
let subscription = observable.subscribe({
    next(val) { console.log("Received key command: " + val) },
    error(err) { console.log("Received an error: " + err) },
    complete() { console.log("Stream complete") },
});
interface Subscription {

    // Cancels the subscription
    unsubscribe() : void;

    // A boolean value indicating whether the subscription is closed
    get closed() : Boolean;
}

Lazy computations

function SomeCrazyComputations () {
    return new Promise(() => {
        // really heavy stuff
    });
}

var result = SomeCrazyComputations().then((data) => {
    // crazy tranformations
    return data;
});


setTimeout(() => {
    result.then(render);
}, 1000);
function SomeCrazyComputations () {
    return new Promise(() => {
        // really heavy stuff
    });
}

let computations = [
    (data) => data.map( ... ),
    (data) => data.filter( ... ),
    (data) => data.reduce( ... )
];

setTimeout(() => {
    [ ...computations, render ].reduce((p, f) => p.then(f), Promise.resolve());
}, 1000);

Generators

EventEmmiter

var events = require("events");
var bus = new events.EventEmitter();

function SomeCrazyComputations (target) {
    target.on("start", () => {
        setTimeout(() => {
            // really heavy stuff
            target.emit("progress", date);
        }, 1000);
    });
}

SomeCrazyComputations(bus);

function SomeCrazyTransformations (target) {
    target.on("progress", (data) => {
        // crazy tranformations
        target.emit("progress-2", data);
    });
});

SomeCrazyTransformations(bus); 

setTimeout(() => {
    bus.on("progress-2", render);
    bus.emit("start", undefined);
}, 1000);

Observables

function listen(element, eventName) {
    return new Observable(observer => {
        // Create an event handler which sends data to the sink
        let handler = event => observer.next(event);

        // Attach the event handler
        element.addEventListener(eventName, handler, true);

        // Return a cleanup function which will cancel the event stream
        return () => {
            // Detach the event handler from the element
            element.removeEventListener(eventName, handler, true);
        };
    });
}
// Return an observable of special key down commands
function commandKeys(element) {
    let keyCommands = { "38": "up", "40": "down" };

    return listen(element, "keydown")
        .filter(event => event.keyCode in keyCommands)
        .map(event => keyCommands[event.keyCode])
}
let subscription = commandKeys(inputElement);
let subscription = commandKeys(inputElement).subscribe({
    next(val) { console.log("Received key command: " + val) },
    error(err) { console.log("Received an error: " + err) },
    complete() { console.log("Stream complete") },
});

Hot and Cold Observables

 

Flow control

How to cancel a Promise?

async function (coords) {
    let countries = await getCountries(coords);
    render(countries);

    let hotels = await getHotelsByCountries(countries);
    render(hotels);

    let facilities = await getFacilitiesByHotels(hotels);
    render(facilities);

    let images = await getImages(facilities);
    render(images);
}
async function (coords, promise) {
    let countries = await Promise.race([getCountries(coords), promise]);
    render(countries);

    let hotels = await Promise.race([getHotelsByCountries(countries), promise]);
    render(hotels);

    let facilities = await Promise.race([getFacilitiesByHotels(hotels), promise]);
    render(facilities);

    let images = await Promise.race([getImages(facilities), promise]);
    render(images);
}

EventEmmiter

 

Try to cancel this code

var events = require("events");
var bus = new events.EventEmitter();

function SomeCrazyComputations (target) {
    target.on("start", () => {
        setTimeout(() => {
            // really heavy stuff
            target.emit("progress", date);
        }, 1000);
    });
}

SomeCrazyComputations(bus);

function SomeCrazyTransformations (target) {
    target.on("progress", (data) => {
        // crazy tranformations
        target.emit("progress-2", data);
    });
});

SomeCrazyTransformations(bus); 

setTimeout(() => {
    bus.on("progress-2", render);
    bus.emit("start", undefined);
}, 1000);
var events = require("events");
var bus = new events.EventEmitter();

function SomeCrazyComputations (target) {
    target.on("start", () => {
        setTimeout(() => {
            // really heavy stuff
            target.emit("progress", date);
        }, 1000);
    });

    target.on("abort", () => { ... });
}

SomeCrazyComputations(bus);

function SomeCrazyTransformations (target) {
    target.on("progress", (data) => {
        // crazy tranformations
        target.emit("progress-2", data);
    });

    target.on("abort", () => { ... });
});

SomeCrazyTransformations(bus); 

setTimeout(() => {
    bus.on("progress-2", render);
    bus.emit("start", undefined);
}, 1000);

Observables

subscription.unsubscribe();
let data = Observable
    .fromPromise(getCountries())
    .flatMap(c => Observable.fromPromise(getHotels(c)))
    .flatMap(h => Observable.fromPromise(getFacilities(h)))
    .flatMap(f => Observable.fromPromise(getImages(f)));

setTimeout(() => {
    data.subscribe( ... );
}, 1000);

setTimeout(() => {
    data.unsubscribe();
}, 3000);

One small example

How much time do you need for Drag and drop

You are a great programmer)

How much lines of code so you need?

let getEltDrags = elt => 
	Observable.fromEvent(elt, "mousedown")
		.map(() => Observable.fromEvent(docucment, "mousemove")
				takeUntil(Observable.fromEvent(docucment, "mouseup")))
		.concatAll();

getEltDrags(img).forEach(ev => {
	img.style.left = ev.pageX;
	img.style.top = ev.pageY;
});

Promise vs Observable

EventEmitter vs Observable

Observer

By Vladimir

Observer

  • 228