## Rob Wormald

### Author of angularSails

irc: robdubya

@robwormald

github.com/robwormald

## We have all kinds of neat tools for working with data

``````var numbers = [1, 2, 3, 4, 5];

function double(number) {
return number * 2;
}

var doubledNumbers = numbers.map(double);
//[2, 4, 6, 8, 10]``````

## .map

``````var numbers = [1, 2, 3, 4, 5];

function greaterThanThree(value) {
return value > 3;
}

var numbersGreaterThanThree =
numbers.filter(greaterThanThree);
//[4, 5]``````

## .filter

``````var numbers = [1, 2, 3, 4, 5];

function sum(total, value) {
}

var total = numbers.reduce(sum, 0);
//15``````

## We use an iterator to access those values.

``````var numbers = [1, 2, 3];

//.values() returns an iterator
var numIterator = numbers.values();

numIterator.next() //{value: 1, done: false }
numIterator.next() //{value: 2, done: false }
numIterator.next() //{value: 3, done: false }
numIterator.next() //{done: true}
``````

## Array as Iterables

``````var myMap = new Map();

myMap.set('a', 1);
myMap.set('b', 2);
myMap.set('c', 3);

//.values() returns an iterator
var valueIterator = myMap.values();

valueIterator.next() //{value: 1, done: false }
valueIterator.next() //{value: 2, done: false }
valueIterator.next() //{value: 3, done: false }
valueIterator.next() //{done: true}

//.keys() returns an iterator
var keyIterator = myMap.keys();

keyIterator.next() //{value: 'a', done: false }
keyIterator.next() //{value: 'b', done: false }
keyIterator.next() //{value: 'c', done: false }
keyIterator.next() //{done: true}

//.entries() returns an iterator
var entryIterator = myMap.entries();

entryIterator.next() //{value: ['a', 1], done: false }
entryIterator.next() //{value: ['b', 2], done: false }
entryIterator.next() //{value: ['c', 3], done: false }
entryIterator.next() //{done: true}
``````

## Map as Iterables

``````function* lazyNumbers(){
yield 1;
yield 2;
yield 3;
}

//generator functions return an iterator
var valueIterator = lazyNumbers();

valueIterator.next() //{value: 1, done: false }
valueIterator.next() //{value: 2, done: false }
valueIterator.next() //{value: 3, done: false }
valueIterator.next() //{done: true}
``````

## Generators

``````class MyCustomList {
constructor(){
this._values = [];
}
[Symbol.iterator](){
var nextIndex = 0;

return {
next: function(){
return nextIndex < this._values.length ?
{value: this._values[nextIndex++], done: false} :
{done: true};
}
}
}
append(val){
this._values.push(val)
}

}

var myList = new MyCustomList();
myList.append(1);
myList.append(2);
myList.append(3);

//.values() returns an iterator
var valueIterator = lazyNumbers();

valueIterator.next() //{value: 1, done: false }
valueIterator.next() //{value: 2, done: false }
valueIterator.next() //{value: 3, done: false }
valueIterator.next() //{done: true}``````

## The event loop pushes values to us.

``````var myButton = document.getElementById('myButton');

function doSomethingOnClick(event){
doSomething(event);

//make sure you remember to tidy up after yourself!
myButton.removeEventListener('click', doSomethingOnClick);
}

``````

## Callbacks are events!

``````getStuff(function(result){
getMoreStuff(function(results){
getSomeStuffForEachResult(results, function(moreStuff){
// note my code running off the page...^
doTheThingWeWantedToDo(moreStuff);
});
});
});``````

## (ugh)

``````function getStuff(url, callback){
var req = new XMLHttpRequest();
function reqListener () {
callback(JSON.parse(req.responseText));
}
req.open("GET", url);
req.send();
}

getStuff('foos.json', function(foos){
getStuff('bars.json', function(bars){
//do stuff with foos and bars
});
});``````

## Uh, Promises? Hello?

``````fetch('foos.json')
.then(function(res){
return res.json();
})
.then(function(foos){
return fetch('bars.json')
.then(function(res){
return res.json();
})
.then(function(bars){
return [foos, bars];
})
})
.then(function(results){
//do stuff with foos and bars
});``````

## Promises don't solve everything.

``````function watchLocation(callback){
return new Promise(function(resolve, reject){
var watchID = navigator.geolocation.watchPosition(function(position) {
resolve([position.coords.latitude, position.coords.longitude]);
});
})

//how do i stop watching?!
}

watchLocation().then(function(location){
///only works once!
})``````

## Observable API

``````//Observable constructor
let myObservable = new Observable(observer => {

//the observer lets us *push* values
observer.next(1);
observer.next(2);
observer.next(3);

//it lets us propagate errors
observer.error('oops');

//and lets us (optionally) complete the stream
observer.complete();

});``````

## Observable API

``````//Observable constructor
let myObservable = new Observable(observer => {

//the observer lets us *push* values
observer.next(1);
observer.next(2);
observer.next(3);

//it lets us propagate errors
observer.error('oops');

//and lets us (optionally) complete the stream
observer.complete();

});``````

## Consuming Observables

``````//Observable constructor
let myObservable = new Observable(observer => {
observer.next(1);
observer.next(2);
observer.next(3);

observer.complete();
});

myObservable.subscribe(
val => console.log(val),
err => console.log(err),
_ => console.log('done')
);``````

## Duality

``````function* values(){
yield 1;
yield 2;
yield 3;
}
let i = values();

i.next() //{value: 1, done: false}
i.next() //{value: 2, done: false}
i.next() //{value: 3, done: false}
i.next() //{done: true}``````
``````let o = new Observable(observer => {

observer.next(1);
observer.next(2);
observer.next(3);

observer.complete();

});

o.subscribe(
value => console.log(value),
err => console.error(err),
_ => console.log('done')
);
``````

## Disposing Observables

``````//Observable constructor
let myObservable = new Observable(observer => {
let count = 0;
let interval = setInterval(() => {
observer.next(count++);
}, 100);

//disposal function
return () => {
clearInterval(interval);
}
});

let subscriber = myObservable.subscribe(
val => console.log(val),
err => console.log(err),
_ => console.log('done')
);

subscriber.unsubscribe();``````
``````const myButton = document.getElementById('myButton');

let clicks\$ = new Observable(observer => {

let onClick = ev => observer.next(ev);

return () => {
myButton.removeEventListener('click', onClick);
}
});

let clickListener = clicks\$.subscribe(event => console.log(event));

//later...
clickListener.unsubscribe();``````

## My First Observable

``````const myButton = document.getElementById('myButton');

let clicks\$ = Observable.fromEvent(myButton, 'click');

let clickListener = clicks\$.subscribe(event => console.log(event));
//ClickEvent
//ClickEvent
//ClickEvent

//later...
clickListener.unsubscribe();``````

## (easy mode)

``````const myInput = document.getElementById('myInput');

//stream of keyup Events
let keyups\$ = Observable.fromEvent(myInput, 'keyup');

//map to the values
let inputs\$ = keyups\$.map(ev => ev.target.value);

inputs\$.subscribe(text => console.log(text));
//h
//he
//hel
//hell
//hello``````

## Inputs - .map

``````const incrementButton = document.getElementById('increment');
const decrementButton = document.getElementById('decrement');
const counterOutput = document.getElementById('output');

const getValue = ev => parseInt(ev.target.value,10);

let increments\$ = Observable.fromEvent(incrementButton, 'click');
let decrements\$ = Observable.fromEvent(decrementButton, 'click');

//merge into a single stream, map to int values
let changes\$ = Observable.merge(increments\$, decrements\$).map(getValue);

//scan (reduce) to track the state
let total\$ = changes\$.scan((total, value) => total + value, 0);

//set count
total\$.subscribe(count => {
counterOutput.innerText = count;
console.log(count);
});``````

## Counter - .reduce/.scan

``````import {Http} from 'angular2/http'
const myInput = document.getElementById('myInput');

//stream of keyups Events
let keyups\$ = Observable.fromEvent(myInput, 'keyup');

//map to the values
let inputs\$ = keyups\$.map(ev => ev.target.value);

//flatMap our inputs to *http responses*
let responses\$ =
inputs\$
.flatMap(text => Http.get(`foo.com/search/\${text}`))
.map(res => res.json());

responses\$.subscribe(text => console.log(text));
//[{name: 'harry'},{name: 'hettie'}, {name: 'hellboy'}];
//[{name: 'harry'}, {name: 'hellboy'}];
//[{name: 'hellboy'}];``````

## Angular2 Http

``````import {Http} from 'angular2/http'
const myInput = document.getElementById('myInput');

//stream of click Events
let clicks\$ = Observable.fromEvent(myInput, 'keyup');

//map to the values
let inputs\$ = clicks\$.map(ev => ev.target.value);

//flatMap our inputs to *http responses*
let responses\$ =
inputs\$
.flatMapLatest(text => Http.get(`foo.com/search/\${text}`))
.map(res => res.json());

responses\$.subscribe(text => console.log(text));
//[{name: 'harry'},{name: 'hettie'}, {name: 'hellboy'}];
//[{name: 'harry'}, {name: 'hellboy'}];
//[{name: 'hellboy'}];``````

## Http - flatMapLatest

``````class MyAutoCompleteComponent {
constructor(http:Http, formBuilder:FormBuilder){

this.searchForm = formBuilder.group({
name: ""
});
this.people = searchForm.controls.name.valueChanges
.flatMapLatest(text => Http.get(`foo.com/search/\${text}`))
.map(res => res.json());
}
}``````

## Angular2 Http + Async Pipe

``````<div [ng-form-model]="searchForm">
<input type="text" ng-control="name">
</div>
<div>
<ul>
<li *ng-for="#person in people | async">{{person.name}}</li>
</ul>
</div>``````
``````let responses\$ =
.retry(3)
.map(res => res.json());

responses\$.subscribe(
res => console.log(res),
err => console.log('couldn't connect!')
);``````

## Angular2 Http Retry

``````let ticks\$ = Observable.interval(5000);

let responses\$ =
ticks\$
.flatMapLatest(() => http.get('stocks.json'))
.map(res => res.json());

let stockPoller = responses\$.subscribe(res => console.log(res));

//later
stockPoller.unsubscribe();``````

## redux

``````export const todos = (state = [], action) => {

switch(action.type){
let todo = Object.assign(
{},
{id: state.reduce((maxId, todo) => Math.max(todo.id, maxId), -1) + 1})
return [...state, todo];
case REMOVE_TODO:
return state.filter(todo => todo.id !== action.payload.id)
case UPDATE_TODO:
return state.map(todo =>
case TOGGLE_TODO:
return state.map(todo =>
case TOGGLE_ALL_TODOS:
return state.map(todo => {
})
case REMOVE_COMPLETED_TODOS:
return state.filter(todo => !todo.completed)
default:
return state;
}
};``````

By Rob Wormald

• 30,418