On Github andrei-yanovich / promise-talk
Created by Andrei Yanovich
Promise is a special state storage object. At first it has - 'pending', after either 'fulfilled' or 'rejected'.
promise object can have 2 types of callbacks
var promise = new Promise(function(resolve, reject) { // do a thing, possibly async, then… if (/* everything turned out fine */) { resolve("Stuff worked!"); } else { reject(Error("It broke")); } }
promise.then(onFulfilled, onRejected)
if a promise was rejected/resolved then his state can't be changed anymore.
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(); }); }
httpGet('talk.json') .then(function(response) { console.log("Success!", response); }, function(error) { console.error("Failed!", error); });
You can chain then's() to transform values or run additional async actions one after another.
var promise = new Promise(function(resolve, reject) { resolve(1); }); promise .then(function(val) { console.log(val); // 1 return val + 2; }) .then(function(val) { console.log(3); // 3 });
httpGet('text.json') .then(JSON.parse) // json.parse takes one arg and returns json .then(function(response) { console.log("Yey JSON!", response); });
function getJSON(url) { return httpGet(url).then(JSON.parse); } getJSON('/data.json') .then(function(data) { return getJSON('api/user/' + data.userId); }) .then(user => { console.log(user); });
If you return another promise inside then(), next then() in queue will be called only after your promise will be resolved.
asyncThing1() .then(function() { return asyncThing2(); }).then(function() { return asyncThing3(); }).catch(function(err) { return asyncRecovery1(); }).then(function() { return asyncThing4(); }, function(err) { return asyncRecovery2(); }).catch(function(err) { console.log("Don't worry about it"); }).then(function() { console.log("All done!"); });
Promise.all([ getJSON('/facebookApi/...'), getJSON('/twitterApi/...'), getJSON('/user/...') ]).then(function(arrayOfResults) { // ... })
var searchTwitter = function(username) { var cache = {}; if (!cache[username]) { cache[username] = getJSON('https://api.twitter.com/1.1/users/search.json?q=' + username); } return cache[username]; } searchTwitter('famousPerson') .then(function(data){ console.log(data.results); //Made a call }); searchTwitter('famousPerson') .then(function(data){ console.log(data.results); //Did NOT make a call });
var wait = function(duration) { return new Promise(function(resolve) { setTimeout(resolve, duration); }) }; wait(500).then(function() { console.log('Called!'); }); // will be called after 500ms
Promises are part of es6(es2015) standart. And they are already implemented in most browsers: Chrome 32, Opera 19, Firefox 29, Safari 8 & Microsoft Edge.
Anyway polyfill is here https://github.com/jakearchibald/es6-promise
Before the standart promises have been around in form of libraries, such as:
The above and other JavaScript promises share a common, standardised behaviour called Promises/A+.
jQuery have similar objects called Deferreds Despite deferreds aren't Promise/A+ compliant, they have rich functionality also.
es6 promises will treat anything with a then method (so called thenable) as promise-like.
var q = Q.defer(), dfd = jQuery.Deferred(); Promise.all([q, dfd]) .then(/* ..... */);
var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.responseType = 'json'; xhr.onload = function() { console.log(xhr.response); }; xhr.onerror = function() { console.log("Booo"); }; xhr.send();
fetch(url).then(function(response) { return response.json(); }).then(function(data) { console.log(data); }).catch(function() { console.log("Booo"); }); //or with es6 arrows fetch(url) .then(r => r.json()) .then(data => console.log(data)) .catch(e => console.log("Booo"))
fetch('/users', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'Hubot', login: 'hubot', }) })
function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response } else { var error = new Error( response.statusText) error.response = response throw error } } function parseJSON(response) { return response.json() } fetch('/users') .then(checkStatus) .then(parseJSON) .then(function(data) { console.log('request succeeded, data) }).catch(function(error) { console.log('request failed', error) })
response.body is a ReadableStream
fetch(url).then(function(response) { return response.json(); });
These are true stream readers, meaning they drain the stream
The call to .text() fails as the stream has already been read. You can work around this using .clone():
fetch(url).then(function(response) { return response.clone().json().catch(function() { return response.text(); }); });