es promise - meetupfiles.meetup.com/18755240/promises - walkthrough.pdf · 2016. 10. 20. ·...
TRANSCRIPT
ES PromiseWalkthrough
@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
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(); }); });}
Y mil cosas más...
Problemas, futures y observables
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 repos.map(repo => repo.name);
})
.then(function(names) {
console.log(names);
});
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((res) => setTimeout(200, res, 'Hello')) .then(x => `${x} world!`) .then(x => get('/foo')) .then(console.log, console.error);
// ------
Future((rej, res) => setTimeout(200, res, 'Hello')) .map(x => `${x} world!`) .chain(x => get('/foo')) .fork(console.error, console.log);
Observable
Observable (RxJS)
Is a set of libraries to compose asynchronous and event-based programs using observable collections and Array#extras style composition in JavaScript.
Some differences with promises
● 0 or many values● Sync or async● Lazy● Can be cancelled
https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/observable.md
// Get stock data somehow
const source = getAsyncStockData();
const subscription = source
.filter(quote => quote.price > 30)
.map(quote => quote.price)
.forEach(price => console.log(`Prices higher than $30: ${price}`);
Thanks!
@san650