JavaScript Meetup - Diciembre 2016files.meetup.com/5844892/Promises+Observables - JS... · Promise...

Post on 31-May-2020

7 views 0 download

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