JavaScript Promises

Promises are the new kid on the block when it comes to handling asynchronous functions in JavaScript. But, before we can talk promises, we have to talk about asynchronous briefly.

Asynchronous Functions And Callback Triangle

Asynchronous functions are functions that handle some request, then return the result when the operation is complete. Now, while this is going on the program is still executing instruction after instruction; execution is not paused. The way these functions were traditionally written were as functions that take other functions.

Here’s an example from a project of mine.

const path = require('path');
const http = require('http');
const URL = require('url').URL;
const RichEmbed = require('discord.js').RichEmbed;
const url = new URL('http://random.cat/meow');
module.exports.run = function ({ client, message, args }) {
/* Get cat function that takes a callback function to
* work with the new cat picture data.
*/
getCat((data) => {
const catEmbed = new RichEmbed({ title: 'Meow' })
catEmbed.setImage(data.file);
message.channel.send("Nyaa ~ :cat: ", { embed: catEmbed })
.then(message => console.log(`Sent message: ${message.content}`))
.catch(console.error);
});
};
/* getCat function that creates an HTTP request (asynchronous)
* and supplies the data to the cat picture.
*/
function getCat(callback) {
http.get({
hostname: url.hostname,
path: url.pathname,
pathname: url.pathname
}, (res) => {
res.setEncoding('utf8');
let rawData = '';
//Passes data to the rawData function.
res.on('data', (data) => {
rawData += data;
});
//Once all data is passed, parse the JSON string that is returned by the end event.
res.on('end', () => {
callback(JSON.parse(rawData));
});
});
}
view raw cat-example.js hosted with ❤ by GitHub

As you can see, if I want to enforce any order for the instructions to be executed, I have to pass a callback function as a parameter. The cat image data will not be returned until a later time; that time is what requires us to use callback functions to accomplish anything with asynchronous functions. Now, this example may seem trivial, but what happens if you have to call this asynchronous function multiple times? Well, that’s when callback hell or the callback triangle comes in.

Here’s an example.

getCat((data) => {
const catEmbed = new RichEmbed({ title: 'Meow' })
catEmbed.setImage(data.file);
message.channel.send("Nyaa ~ :cat: ", { embed: catEmbed })
.then(message => console.log(`Sent message: ${message.content}`))
.catch(console.error);
/* ES5 Function */
getCat(function (data) {
/* Cat data 2 operations… */
getCat((data) => {
/* Cat data 3 operations… */
getCat((data) => {
/* Cat data 4 operations…
* Okay, too many callbacks now.
* Oh look a triangle.
*/
});
});
});
});
view raw cat-example2.js hosted with ❤ by GitHub

As you can see again, I have to use callbacks to enforce order creating a triangle of callback functions. Now, this is the problem promises solve in JavaScript, so let’s go in-depth on promises. Let’s rewrite the original asynchronous function to return a promise.

Promises – How They Solve The Problem

Promises expose a simple way to handle asynchronous functions through the use of then and catch methods. Now, the way it works is that a promise takes a single function with a resolve function and reject function as parameters. When the promise resolves the parameter of the resolve function is passed to the then method. If the promise is rejected, it’s passed to the catch method. Now, one other important feature is that the then method can be called multiple times to transform the result of the promise.

Anyway, the best way to demonstrate is an example, so here’s an example of the original function rewritten with promises.

const path = require('path');
const http = require('http');
const URL = require('url').URL;
const url = new URL('http://random.cat/meow');
/* getCat function that creates an HTTP request (asynchronous) and
* supplies the data of the cat picture through a promise.
*/
function getCat() {
return new Promise((resolve, reject) => {
http.get({
hostname: url.hostname,
path: url.pathname,
pathname: url.pathname
}, (res) => {
res.setEncoding('utf8');
let rawData = '';
res.on('data', (data) => {
rawData += data;
});
res.on('end', () => {
/* Runs the resolve function when the request ends. */
resolve(JSON.parse(rawData));
});
res.on('error', (err) => {
console.log(err);
/* Runs the reject function if an error occurs. */
reject(err);
});
});
});
}
/* Get cat function that returns a promise. */
//ES6
getCat().then(data => {
console.log(data);
});
//ES5
getCat().then(function (data) {
console.log(data);
});

As you can see, we work through the promise and use then to interact with the values of the promise. You can run this in Node.js and see the data, you can also add more then methods for chaining. This chaining, allows us to create the order we want when working with asynchronous functions.

With that said, that covers the basics of promises, but there are definitely more useful functions.  If you have anything to add a comment down below; I’ll gladly answer.

%d bloggers like this: