0.3 - Callbacks & Promises
šÆ Objectives
- Differentiate between callbacks, promises, and async/await.
- Write an asynchronous program using callbacks functions.
- Write an asynchronous program using promises.
- Substitute promising chaining with the
asyncandawaitkeywords.
šØ Setup
-
Open Visual Studio Code and open a terminal (
CTRL + `). -
Create a new folder
mkdir ~/web-ii/exercises/0.3-callbacks-promises. -
Navigate inside that folder, run
npm init -y, and add"type": "module"like previous times. -
Create
pokemonDatabase.jswith the following contents:pokemonDatabase.js export const pokemonDatabase = [{ name: "Bulbasaur", type: "Grass" },{ name: "Squirtle", type: "Water" },{ name: "Charmander", type: "Fire" },];It doesnāt have to be Pokemon name/type! Thatās just what I like. Feel free to use actor/movie, artist/album, author/book, or anything else that you find interesting.
š Context
In E0.1 we saw how we can write non-blocking/asynchronous JavaScript. This has traditionally been done through the use of callback functions. As youāll experience through this exercise, callback functions are prone to a problem known as callback hell where code becomes unreadable and unmaintainable very quickly. Promises were implemented to try and mitigate this problem.
What exactly is a Promise in JavaScript?

Courtesy of Lydia Hallie. Her article on this topic goes far more in depth than this exercise does. The article is eloquently written and I aspire to reach her level of diagrams. I highly recommend reading it (after finishing this exercise) if youāre curious about the nitty gritty of async programming in JS!
Weāll start by coding our own callback system to asynchronously create new Pokemon in a ādatabaseā (which will just be an array of objects) . From there, weāll turn that into the equivalent structure using promises and explore how we can use promises for both sequential and simultaneous operations. Lastly, weāll simplify the promise by using the async and await keywords.
š¦ Letās Go
Callbacks
-
Create a file called
callbacks.jsin the folder for this exercise. -
Import the
pokemonDatabasefrom the file created in the setup section above. -
Declare a function named
fetchPokemon():- This function will take no parameters.
- Declare a variable called
fetchTimeand initialize it to a random number between 1 and 500.- This number will control how long the
setTimeout, outlined below, will take to execute. - The randomness is meant to mimic the fluctuation of network speeds.
- This number will control how long the
- This function will print the contents of the
pokemonDatabaseto the terminal afterfetchTimemilliseconds go by. You should use the setTimeout function to achieve this.
-
Declare a function named
createPokemon(pokemon):- This function will take one parameter:
pokemonas an object. - Declare a variable called
createTimeand initialize it to a random number between 1 and 500. This number will control how long thesetTimeout, outlined below, will take to execute. The randomness is meant to mimic the fluctuation of network speeds. - This function will push the
pokemonobject into thepokemonDatabasearray aftercreateTimemilliseconds go by. You should use the setTimeout function to achieve this.
- This function will take one parameter:
-
Call the
createPokemonfunction and pass in an object that contains anameproperty and atypeproperty. The values of these properties are up to you. -
Call
createPokemonagain with a different object. -
Call the
fetchPokemonfunction. -
Run the program in the terminal (
node callbacks.js) several times. If you coded everything correctly until this point, you should see the new Pokemon being printed randomly upon each program execution:
This is because weāre using random values for
fetchTimeandcreateTime. IfcreateTimeis greater thanfetchTime, it means that thecreatePokemonfunction will take longer to run. If it takes longer to run thanfetchPokemon, then it should make sense that whenfetchPokemonprints, it does not have the new Pokemon inside ofpokemonDatabaseto print, so it only prints the original three.Ideally, we would like to have the new Pokemon displayed 100% of the time, and in the right order. How can we fix this problem? You guessed it, callbacks! š¤©
-
Modify the
createPokemonfunction declaration to take a second parameter calledcallback. -
Call the
callback()after the new Pokemon has been inserted into thepokemonDatabasearray. -
Modify the first call to
createPokemonsuch that you pass in a callback as the second parameter. That callback should invoke the secondcreatePokemonwhere you pass in a new Pokemon object as the first parameter and thefetchPokemonfunction as the second parameter.
-
Run the program (
node callbacks.js) several times now and confirm that the new Pokemon are being printed 100% of the time, and in the same order every time.
Callback Hell
The trouble with callback functions is that they can be nested quite easily. When your callback has its own callback that has its own callback, ad infinitum, you can imagine how deep the rabbit hole can go!

This phenomenon has been affectionately dubbed as ācallback hellā and itās one of the main reasons why developers came up with promises! š®
Promises
Letās see how we can get rid of callback hell by using promises.
-
Make a duplicate of
callbacks.jsand call itpromises.js. -
At the end of the
createPokemonfunction, return anew Promise():- The promise will take one parameter as input: a callback function.
- The callback will take 2 parameters as input called
resolveandreject. - Move the
setTimeoutfunction entirely into the body of the promiseās callback. - Change
callback()toresolve()inside ofsetTimeout. - Remove the
callbackparameter from the parameter list ofcreatePokemon.
-
Remove the calls to
createPokemonandfetchPokemonfrom the bottom of this file and replace them with one call tocreatePokemon, passing in the one Pokemon object it normally takes. -
Now, because
createPokemonreturns aPromiseobject, we can call thethen()method on it.- We can pass in a callback to
then()and that callback will be invoked if the previousPromisewas successfully fulfilled. - If the return value of the callback passed to
then()returns a promise, we can chain anotherthen()onto it. This secondthen()can take a new callback, which can return a promise, so we can chain anotherthen(), ad infinitum.

- We can pass in a callback to
-
Run this program (
node promises.js) several times now and confirm that the new Pokemon are being printed 100% of the time, and in the same order every time.
Promise.all
Chaining promises using then is great if we want multiple operations to run sequentially, one after the next. However, by doing this, we lose the ability to have multiple operations run simultaneously, at the same time. Letās look at how we can perform operations simultaneously using Promise.all()!
-
Make a duplicate of
promises.jsand call itpromise-all.js. -
Inside of
promise-all.js, remove the block of chained promises from the end of the file. -
Declare a new array called
pokemonPromises. Each element of this array will be a call tocreatePokemon. -
After the array, call
Promise.all(pokemonPromises). This function takes an array of promises, and waits for all promises in the array to be fulfilled. Once all promises in the array are fulfilled,Promise.allreturns a promise of its own. We can callthenon this returned promise to execute something after all the simultaneous operations have finished. In this case, we want to executefetchPokemonafter all the Pokemon have been created. -
Run this program (
node promise-all.js) several times and confirm that the new Pokemon are being printed 100% of the time, but not in the same order every time. It should make sense why the output is different each time you run the program. Since we are running allcreatePokemonoperations at the same time, and each one of them takes a different time to run, we will get a different output each time.
We can compare the time difference between sequential operations and simultaneous operations by using console.time():

The execution time was cut in half! š
Async/Await
After a couple of years of using promises, JS developers found it cumbersome to have to chain the .then() calls. Theyāre definitely better than using callbacks, but it would be nice if we could write asynchronous code in the same way we write synchronous code. In an attempt to do this, the keywords async and await were born. These keywords are syntactic sugar for using promises.
-
Make a duplicate of
promises.jsand call itasync-await.js. -
Inside of
async-await.js, remove the block of chained promises from the end of the file. -
Declare a function called
createAllPokemon():- In the body of the function, call
createPokemon, passcreatePokemona new object, but donāt callthen()oncreatePokemon. Instead, add the keywordawaitin front of the call.- The
awaitkeyword is essentially a (nicer looking) replacement for usingthen(). The program will wait for this promise to be fulfilled before moving on with execution.
- The
- Repeat this for several more calls to
createPokemon. - Finally, call
fetchPokemon. SincefetchPokemondoes not return a promise, we donāt have toawaitit.
- In the body of the function, call
-
We can only use
awaitinside of a function that has been declared asasync. To do this, write the keywordasyncbefore the function declaration. -
Call
createAllPokemonat the end of the file.
-
Run this program (
node async-await.js) several times and confirm that the new Pokemon are being printed 100% of the time, and in the same order every time.
š„ Submission
Take a screenshot of all four programs being run with the time that each program took to execute and submit it on Moodle. Again, you can use console.time() to accomplish this. Put console.time('timer label') before calling any function, and put console.timeEnd('timer label') inside of fetchPokemon just after it finishes printing all the Pokemon to the terminal.

Submit the screenshot in the Moodle drop box called Exercise 0.3 - Callbacks & Promises.
Asynchronous programming can be tough to wrap your head around, thatās for sure. Hopefully you can appreciate the advantages it provides and how it unlocks a whole other world of possibilities!
