Tutorial: Javascript Promises: There and Back Again in Half the Time
If you are familiar with javascript, the chances are that you have found this post. I found it to be a great introduction to promises in javascript, but I also found the post to be rather long and not good as a reference. This post, however is an attempt to condense the information found in the tutorial from html5rocks (Update! HTML5 Rocks has been turned into the Google Developers Web Fundamentals) into a nice neat package that is not only useful as a tutorial, but also useful as a reference.
The Problem and the Solution
When performing asynchronous tasks in javascript, you usually have to use callbacks and event listeners. This can quickly get unwieldy and difficult to understand. Promises were invented to rectify this problem. They allow you to chain function calls asynchronously - which generally cleans up your code and makes it easier to read:
function get(url)
{
return new Promise(function(resolve, reject) { //important!
var ajax = new XMLHttpRequest();
ajax.onload = function() {
if(ajax.status >= 200 && ajax.status < 300)
resolve(ajax.response); //return the response
else
reject(ajax.response);
}
});
}
The important bit is the returning of a promise instead of actually performing the action requested. When creating a promise, a function is taken as the only argument. This function is then called with 2 arguments: a function that should be called upon success (usually called resolve()
), and a function that should be called upon failure (usually called reject()
).
Any number of asynchronous actions can be performed before either resolve()
or reject()
are called, but I would adivse that you put one async call in each promisified function.
The resolve()
and reject()
functions take a single argument: the value that should be passed onto the next function in the chain.
and then....
Once a promise has been returned, an API is exposed that allows you to chain functions together in a long string through the function then()
:
// then() adds a function to the chain
// |--------v
get("https://starbeamrainbowlabs.com/humans.txt").then(function(response) {
//we got a response :D
console.info("Got response", response);
}, function(error) {
//something went wrong :(
console.error("Something went wrong:", error);
});
then()
takes a pair of functions as it's arguments. The first function is called upon success (and passed the value that was passed to resolve()
), and the second (optional) function is called upon failure (and passed the value that was passed to reject()
).
Repetition
The best part of javascript promises is the ability to chain then()
as many times as you like:
get("https://example.com/urls.txt").then(function(text) {
return text.split("\n");
}).then(function(lines) {
lines.forEach(function(url) {
get(url).then(function(response) {
console.log("got", url, "with text", response);
});
});
});
Infinite chaining
The problem with the above is that lots of ajax calls a being made at once. If you used this in your next big web app, it could bring your server down having all the requests being sent out at once.
There is, however, a solution to this too. We can process each value in an array one at a time using Array.reduce()
:
get("https://example.com/urls.txt").then(function(text) {
return text.split("\n");
}).then(function(lines) {
lines.reduce(function(sequence, url) {
return squence.then(function() {
return get(url);
}).then(function(response) {
console.info("got", response);
});
}, function() { return new Promise(function(resolve) { resolve(); }); });
});
In this way, a new promise is created that resolves straight away (you can also use the Promise.resolve
function is a shortcut here, but some implementations - especially Node.JS based ones - don't support this), and a promise for each url are then tacked onto the end of it. These are then called in a row - once th eprevious ajax request has completed, the next one will be sent. This can also be helpful when you are fetching content from a list of urls and you want them to be kept in order.
Conclusion
Promises are cool - they let you simplify complex asynchronous code. You can chain them with then()
as many times as you like - and you can use Array.reduce()
to chain calls that iterate over each value in an array.
So there you have it! A short tutorial on javascript promises. If you do not understand anything in this tutorial, please leave a comment below and I will try to answer your question.
Found a mistake? Leave a comment below.