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();
});
});