Dealing with async actions in JavaScript

Before promises become a thing in JavaScript we had to rely on callback functions if we needed something to be done after an asynchronous action. Let's say we have two functions and we want them to run in sequence, but the first function has some async action happening inside it such as fetching data from a rest api. This is how we did it in the past:

function firstFunction(callback) { 
    // setTimeout() is used to simulate async action   
    setTimeout(() => {
        console.log("first done");
        callback();
    }, 500);
}
    
function secondFunction() {
    console.log("second done");
}
    
firstFunction(secondFunction); 
// logs out: "first done" "second done"

With promises we can do the same without using callback functions:

function firstFunction() {    
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log("first done");
            resolve();
        }, 500);
    });
}
    
function secondFunction() {
    console.log("second done");
}
    
firstFunction().then(secondFunction); 
// logs out: "first done" "second done"

In other words, promises allow us to chain functions using their then() method which is a cleaner syntax than using callback functions, specially when more async functions need to run after one another so you can chain them like this:

// using callbacks 
firstFunction(
    secondFunction(
        thirdFunction(
            fourthFunction();
        );
    );
);

// using promises
firstFunction()
.then(secondFunction)
.then(thirdFunction)
.then(fourthFunction);

With await keyword available in JavaScript now you can run async functions like normal functions without chaining. The only catch is that await keyword can only be used inside an async function so we need a async function wrapper around our code like this:

async () => {
  await firstFunction();
  await secondFunction();
  await thirdFunction();
  console.log('all async stuff are done');
}

If you have several async functions which do not depend on one another, a more performant approach would be to execute them in parallel using Promise.all():

async () => {
  await Promise.all([
    firstFunction(), 
    secondFunction(), 
    thirdFunction()
  ]);
  console.log('all async stuff are done');
}