Async JS simplified, I promise.

NOTICE: Before reading this post it is recommended that you read this post prior as it introduces async code in JavaScript.

In this scenario, we’re using a 3rd party library called lib.js to build an app. We’d like to use its methods printRed(), printBlue() and printGreen(). We’ve also observed that these methods run asynchronous each with different execution times. So when they are called like this:

lib.PrintBlue("Blue");
lib.PrintGreen("Green");
lib.PrintRed("Red");

The output is: Green, Red, Blue

Luckily the authors of lib have documented that these methods return a promise that resolves after the string passed to the method is printed.

While I can go more in depth into promises itself, I’d rather leave that for a later post. Instead, I’ll focus on how to use promises. In order to use a promise; you call the .then() method and pass a callback function to it. The callback function would then receive the result that the promise resolves to.

lib.printBlue("Blue")
	.then(()=>lib.printGreen("Green"))
	.then(()=>lib.printRed("Red"));

We’ll be using arrow anonymous functions in these examples because they are more succinct than writing function(){}.

.then() can be called repeatedly after to synchronize subsequent async calls, this is called promise chaining.

The output would now look like: Blue, Green, Red

sync output
Output with promises

If the a callback in promise chain returned a value then it would be received by the subsequent callback as a parameter in the promise chain.
I’ll demonstrate this with lib’s add() method that returns a promise that resolves with the sum of two parameters sent to add();

function sum(a, b, c, d, callback){
	lib.add(a, b)
	.then(absum=>lib.add(absum, c))//res = a + b
	.then(abcsum=>lib.add(abcsum, d))//res = a + b + c
	.then(callback);//callback receives a + b + c + d
}
sum(1, 2, 3, 4, console.log);//Output: 10

Promise.catch()

A promise that has executed successfully is said to be resolved. However, if a promise failed for some reason, it is said to be rejected. The callback given to the then() method executes when a promise has resolved. You can also specify a second callback as an error handler which executes when the promise is rejected.

lib.printBlue("Blue")
     .then(()=>console.log("Success"), ()=>console.log("failed");

If you have a long promise chain it could be tedious to create a handler for each then() call. Therefore the .catch() method can be used to catch any errors which may occur in any of previous promises in the chain.

function sum(a, b, c, d, callback){
	lib.add(a, b)
	.then(absum=>{
		if(typeof c != 'number')throw `${c} is not a number!`;
		return lib.add(absum, c);
	})
	.then(abcsum=>{
		if(typeof d != 'number')throw `${d} is not a number!`;
		return lib.add(abcsum, d);
	})
	.catch(e=>e)//catch error and return it
	.then(callback);//receives either the sum or an error message
}
sum(1, 2, 'l', 4, console.log);
//OUTPUT: l is not a number!

NB Callbacks is in the single line arrow function syntax with no curly braces automatically return the value of that line. When curly braces are used a value must be explicitly returned.

Promise.all()

Lib also happens to provide methods that retrieves user records as a collection or by id. Our app for some reason needs a function to retrieve specific users and pass it to a callback function. You might be tempted to do something like this.

function getTrio(id1, id2, id3, callback) {
	let trio = [];
	lib.getUser(id1).then(
		user1=>{
			trio.push(user1);
			lib.getUser(id2).then(user2=>{
				trio.push(user2);
				lib.getUser(id3).then(user3=>{
					trio.push(user3);
					callback(trio);
				})
			});
		});
}

getTrio(1,2,3, function(users){
    console.log(users);
});

We can also rewrite this using a promise chain to avoid the deep nesting.

function getTrio(id1, id2, id3, callback) {
	let trio = [];
	lib.getUser(id1)
		.then(user1=>{
			trio.push(user1);
			return lib.getUser(id2);//return a promise to be handled in the next then() call
		})
		.then(user2=>{
			trio.push(user2);
			return lib.getUser(id3);
		})
		.then(user3=>{
			trio.push(user3);
			callback(trio);
		});
}

These work, but these promises are independent of each other so we can still run them asynchronously. However we need to pass the array of values when all of the promises have resolved. This is what Promise.All() can do for us.

function getTrio2(id1, id2, id3, callback) {
	let trio = [
		lib.getUser(id1),
		lib.getUser(id2),
		lib.getUser(id3)
	];//array of promises
	Promise.all(trio).then(callback);//receives array of user objects
}
getTrio(1,2,3, function(users){
	console.log(users);
})
getTrio() Output

Promise.race()

Next we’ll take a look at another method on promise called race(). Race() lets you start multiple promises at the same time but would resolve only to the promise which finishes first.

let promises = [
     lib.printRed("Red"),
     lib.printBlue("Blue"),
     lib.printGreen("Green"),
];
Promise.race(promises).then(()=>{
	console.log("fastest promise completed");
});

We know print green is the fastest, so we should see the console log message printed after print green executes then the other print messages.

Lib.js is openly available if you wish to try out these exercises yourself.

Many traditionally callback based APIs are opting to provide promised based versions. Hopefully this helps you to get started on handling promises.

Leave a comment