javascript meetup - diciembre 2016files.meetup.com/5844892/promises+observables - js... · promise...
TRANSCRIPT
Promises and ObservablesJavaScript Meetup - Diciembre 2016
Matías Delgado | @matias_delgado
Santiago Ferreira | @san650
Promise
A Promise represents a value which may be available now, or in the future, or never.
A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers to an asynchronous action's eventual success value or failure reason.
ECMA Script Promise
http://caniuse.com/#search=promise
var promise = new Promise(function(resolve, reject) {
// call resolve or reject
});
promise.then(function(value) {
// work with value
}, function(error) {
// on error
});
Basic usage (I)
function get(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest();
req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); });}
Basic usage (II)
function get(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest();
req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); });}
Basic usage (III)
https://github.com/san650?tab=repositories&type=source
https://api.github.com/users/san650/repos
[ { "id": 40941202, "name": "ember-sokoban", ... }, { "id": 40941203, "name": "tajpado", ... }, ...]
get('https://api.github.com/users/san650/repos')
.then(function(response) {
var repos = JSON.parse(response);
console.log(repos.map(repo => repo.name));
});
Basic usage (IV)
¿Y si hay un error en la petición?
function get(url) { return new Promise(function(resolve, reject) { var req = new XMLHttpRequest();
req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); });}
get('https://api.github.com/users/san650/repos') .then(function(response) { var repos = JSON.parse(response); console.log(repos.map(repo => repo.name)); }, function(error) { console.warn(error); });
Error handling (I)
get('https://api.github.com/users/san650/repos') .then(function(response) { var repos = JSON.parse(response); console.log(repos.map(repo => repo.name)); }) .catch(function(error) { console.warn(error); });
Error handling (II)
.catch(function(reason) {})
=
.then(undefined, function(reason) {})
Error handling (III)
get('https://api.github.com/users/san650/repos')
.then(function(response) {
return JSON.parse(response);
})
.then(function(repos) {
return repos.map(repo => repo.name);
})
.then(function(names) {
console.log(names);
}); Transforming values (I)
get('https://api.github.com/users/san650/repos') .then(JSON.parse) .then(function(repos) { return repos.map(repo => repo.name); }) .then(console.log);
Transforming values (II)
get('https://api.github.com/repos/san650/ceibo') .then(JSON.parse) .then(function(repo) { return get(repo.stargazers_url); }) .then(JSON.parse) .then(function(stars) { console.log(stars.length); });
Queuing async actions (I)
get('https://api.github.com/repos/san650/ceibo') .then(JSON.parse) .then(function(repo) { return get(repo.stargazers_url); }) .then(JSON.parse) .then(function(stars) { console.log(stars.length); });
Queuing async actions (II)
get('https://api.github.com/users/san650/repos') .then(function(response) { // ... then 1 }) .catch(function(error) { // ... catch 1 }) .then(function(message) { // ... then 2 });
Ejercicio para el lector
Then 1
get('...')
Catch 1Then 2
Unhandled errorDone
Promise.all([ get('https://api.github.com/repos/san650/ceibo'), get('https://api.github.com/repos/san650/ombu')]).then(function(responses) { // array of responses});
Promise.all(iterable)
Promise.race([ get('https://api.github.com/repos/san650/ceibo'), get('https://api.github.com/repos/san650/ombu')]).then(function(response) { // only one response});
Promise.race(iterable)
Promise.resolve('a dummy value');
Promise.resolve(value)
Promise.reject(Error('a failed promise'));
Promise.reject(reason)
ECMAScript Promise
● new Promise(function(resolve, reject))
● then, catch
● chaining
● Promise.all
● Promise.race
● Promise.resolve
● Promise.reject
RSVP.Promise
function get(url) { return new RSVP.Promise(function(resolve, reject) { var req = new XMLHttpRequest();
req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); });}
Basic usage (I)
function get(url) { return new RSVP.Promise(function(resolve, reject) { var req = new XMLHttpRequest();
req.open('GET', url); req.onload = function() { if (req.status == 200) { resolve(req.response); } else { reject(Error(req.statusText)); } }; req.onerror = function() { reject(Error("Network Error")); }; req.send(); });}
Basic usage (II)
var deferred = RSVP.defer();
// ...deferred.promise // access the promise
// ...deferred.resolve();
Deferred (I)
var deferred = RSVP.defer();
btn.onclick = function() { deferred.resolve(42);};
deferred.promise.then(function(value) { console.log(`the answer to life the universe and everything is ${value}`);});
Deferred (II)
get('https://api.github.com/users/san650/repos') .then(function(response) { // process }) .catch(function(error) { // an error }) .finally(function() { // always runs });
finally
RSVP.hash({ ceibo: get('https://api.github.com/repos/san650/ceibo'), ombu: get('https://api.github.com/repos/san650/ombu')}).then(function(data) { // data.ceibo // data.ombu});
RSVP.hash
RSVP.hashSettled({ ceibo: get('https://api.github.com/repos/san650/ceibo'), ombu: get('https://api.github.com/repos/san650/ombu')}).then(function(data) { // data.ceibo -> { state: 'fulfilled', value: value } // data.ombu -> { state: 'rejected', reason: reason }});
RSVP.hashSettled
RSVP.allSettled([ get('https://api.github.com/repos/san650/ceibo'), get('https://api.github.com/repos/san650/ombu')]).then(function(responses) { // responses[0] -> { state: 'fulfilled', value: value } // responses[1] -> { state: 'rejected', reason: reason }});
RSVP.allSettled
RSVP.on('created', function(event) { // event.detail // event.label // ...});
RSVP.on('fulfilled', function(event) { // ...});
RSVP.on('rejected', function(event) { // ...});
Instrumentation
Demo ember-inspector
new RSVP.Promise(function(resolve, reject) {}, "a label");
RSVP.Promise.resolve('a value', 'a label');
RSVP.on('created', function(event) { // event.label});
Labelling
RSVP.on('error', function(reason, label) { if (label) { console.error(label); }
console.assert(false, reason);});
Unhandled errors
RSVP.Promise
● Deferred
● finally
● RSVP.hash
● RSVP.allSettled
● RSVP.hashSettled
● RSVP.on(...) instrumentation
● RSVP.on('error') unhandled errors
● labelling
Bluebird
get('https://api.github.com/repos/san650/ceibo') .timeout(100) .catch(Promise.TimeoutError, function() { // could not get response within 100ms }) .catch(function() { // different error });
timeout
get('https://api.github.com/repos/san650/ceibo') .timeout(100) .catch(Promise.TimeoutError, function() { // could not get response within 100ms }) .catch(function() { // different error });
Catch with "type matching"
Cancellation
function get(url) { return new Promise(function(resolve, reject, onCancel) { var xhr = new XMLHttpRequest(); ... onCancel(function() { xhr.abort(); }); });}
get('https://api.github.com/repos/san650/ceibo').cancel()
Y mil cosas más...
Problemas y futures
Problemas...
var promise = get('https://api.github.com/users/san650/repos');
btn.onclick = function() {
promise.cancel(); // nope :-(
};
Cancellation
var promise = new Promise(function(resolve, reject) {
// ...
}).then(function() {
// ...
}).then(function() {
// ...
}).then(function() {
// ...
}).then(function() {
// ...
}); Performance
get('https://api.github.com/users/san650/repos')
.then(function(response) {
return JSON.parse(response);
})
.then(function(repos) {
return Promise.all(...);
})
.then(function(values) {
console.log(values);
});
Inconsistencias, no es lo mismo retornar un valor que retornar una promesa
Problemas
● No se pueden cancelar
● No son lazy, la función executor se ejecuta siempre
● Performance
● Inconsistencias, no es lo mismo retornar un valor que retornar una promesa
Futures
Futures (Fluture)
Much like Promises, Futures represent the value arising from the success or failure of an asynchronous operation (I/O).
Though unlike Promises Futures are lazy and monadic by design. They conform to the Fantasy Land algebraic JavaScript specification.
https://github.com/Avaq/Fluture/wiki/Comparison-to-Promises
new Promise(function(resolve, reject) { resolve('san650/repos');}).then(function(value) { return `https://api.github.com/users/${value}`;}.then(function(url) { return get(url); // returns a new Promise}).then(function(repos) { JSON.parse(repos).map(repo => console.log(repo.name));}, console.error);
https://github.com/Avaq/Fluture/wiki/Comparison-to-Promises
Future(function(reject, resolve) { resolve('san650/repos');}).map(function(value) { return `https://api.github.com/users/${value}`;}).chain(function(url) { return get(url); // returns a new Future}).fork(console.error, function(repos) { JSON.parse(repos).map(repo => console.log(repo.name));});
Fin primera parte...
Observables
¿Qué son los Observables?
Patrón Observer
Se agregan los eventos:
● onError
● onComplete
Ejemplo con RxJS
let observable = Observable.create((observer) => { setTimeout(() => { observer.onNext(1891); // onComplete, onError }, 500);});
observable.subscribe((value) => { ...});
Ejemplo con RxJS
let observable = Observable.create((observer) => { setTimeout(() => { observer.onNext(1891); // onComplete, onError }, 500);});
observable.subscribe((value) => { ...});
Ejemplo con RxJS
let observable = Rx.Observable.create((observer) => { setTimeout(() => { observer.onNext(1891); // onComplete, onError }, 500);});
observable.subscribe((value) => { ...});
Promises recargadas
Múltiples valores
● Los Observables pueden devolver múltiples valores durante cierto periodo de tiempo.
● Ideal para trabajar con datos en tiempo real, eventos, o streams en general.
Cancelablelet observable = Rx.Observable.create((observer) => { let id = setInterval(() => { observer.onNext(1891); }, 500);
// clean up return () => { console.log("disposed"); clearTimeout(id); };});
let disposable = source.subscribe((value) => { // ...});
disposable.dispose();
Cancelablelet observable = Rx.Observable.create((observer) => { let id = setInterval(() => { observer.onNext(1891); }, 500);
// clean up return () => { console.log("disposed"); clearTimeout(id); };});
let disposable = source.subscribe((value) => { // ...});
disposable.dispose();
Cold vs Hot Observables
Cold: Lazy y repetibles
let observable = Rx.Observable.create((observer) => { // Not executed setTimeout(() => { observer.onNext( 1891); }, 500);});
// observable.subscribe((value) => {// ...// });
Hot Observables
● Emiten eventos incluso sin suscriptores● No repetibles● Ejemplo: eventos del DOM
Basado en streams
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
let source = Observable.fromEvent(myInput, "keyup") .pluck("target", "value") .filter(value => value.length >= 3) .debounceTime(500) .mergeMap(term => search(term)) .subscribe( data => { console.log("Subscribe: ", data); }, error => { console.log("Error: ", error); });
Unificación de modelos
● Se pueden crear Observables a partir de:○ Llamadas ajax○ Eventos del DOM○ Workers
Observables? RX?
● Propuesto para agregar al standard● RxJS es una implementación
RxMarbles
http://reactivex.io/documentation/operators/debounce.html
+140 operadores
Demo Time!
Resumiendo...
● Es el patrón Observer bien hecho● Promises recargadas● Trabaja de streams● Unificación de modelos (ajax, eventos, workers)
¿Preguntas?
Enlaces (observables)
● https://medium.com/@benlesh/hot-vs-cold-observables-f8094ed53339#.e8e9uecgy
● http://reactivex.io/documentation/observable.html● https://github.com/tc39/proposal-observable● http://blog.thoughtram.io/angular/2016/06/16/cold-vs-hot-observab
les.html#hot-vs-cold-observables● https://github.com/Reactive-Extensions/RxJS/tree/master/doc/api/
core/operators● https://scotch.io/tutorials/angular-2-http-requests-with-observables● http://rxmarbles.com● https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
Enlaces (promises)
● https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
● https://github.com/tildeio/rsvp.js/● http://bluebirdjs.com/docs/getting-started.html● https://github.com/Avaq/Fluture● https://github.com/Avaq/Fluture/wiki/Comparison-to-Promises
Thanks!
@EmberMontevideo
@angularMVD