Promises
The asynchronous nature of JavaScript does take some time to get used to. Any time we need to wait for a resource, or wait for user input, we need to implement a callback mechanism to handle this correctly. Unfortunately, as a code base grows, we find that we need to rely on callbacks more and more. This can easily lead to what is known as callback hell, where we have so many callbacks that are nested in other callbacks that the code becomes increasingly difficult to read and maintain.
As an example of this, let's consider some code that must read three files one after the other, and print their contents, as follows:
import * as fs from "fs";
fs.readFile("./test1.txt", (err, data) => {
if (err) {
console.log(`an error occurred : ${err}`);
} else {
console.log(`test1.txt contents : ${data}`);
fs.readFile("./test2.txt", (err, data) => {
if (err) {
console.log(`an error...