JavaScript, Snippets

The same code with Callbacks vs Promises vs Async/Await

Sometimes in JavaScript to have to deal with different flavours of asynchronous code, so it is handy to be able to map back and forth between them.

Callback

Functions that do something asynchronously are typically implemented using the callback pattern, and their implementation might look like this:

const myAsyncFunction = (parameter, callback) => {
    const result = //do something async here...
    callback(result, error);
};

They may be invoked in the following way:

const main = () => {
    myAsyncFunction("parameter", (result, error) => {
       //use the result or error here
    });
};

However, things may quickly get out of hand if we need the result of an asynchronous function to invoke another asynchronous function, and then we need that to invoke another one and so on…:

const main = () => {
    myAsyncFunction("parameter", (result, error) => {
       myOtherAsynchronousFunction(result, (otherResult, otherError) => {
           myFinalAsynchronousFunction(otherResult, (finalResult, finalError) => {
              //whew!
           });
       });
    });
};

Promise

We can use Promises to solve the indentation mess above. For example, this is how we might change our main function if we want to wrap the myAsyncFunction in a promise

const main = () => {

    const myPromise = new Promise((resolve, reject) => {
        myAsyncFunction("parameter", (result, error) => {
            if (!error) { //or other equivalent check
                resolve(result);
            } else {
                reject(error);
            }
        });
    });

    myPromise
    .then((result) => { /* handle result here */ })
    .catch((error) => { /* handle error here */ })
};

Promises can be chained, so now we don’t have to use nesting in order to use the result of an asynchronous operation:

const main = () => {

   //...

    myPromise
    .then(myOtherPromise)
    .then(myFinalPromise)
    .catch((error) => { /* handle error here */ })
};

More on Promises and chaining in the official docs.

Async/Await

Once we are already dealing with promises, it is possible to ditch the .then() and .catch() functions from the Promise API and use the async/await syntactic sugar:

const main = async () => {

    const myPromise = new Promise((resolve, reject) => {
        myAsyncFunction("parameter", (result, error) => {
            if (!error) { //or other equivalent check
                resolve(result);
            } else {
                reject(error);
            }
        });
    });

   const result = await myPromise;
};

(Remember to change the main function to be async!)

This way the code can look much more similar to synchronous code

const main = async () => {

   //...
   const result = await myPromise;
   const otherResult = await myOtherPromise;
   
   return result + otherResult;
};