JavaScript Callbacks and Promises | Clear and Simple definition

Bilal Kazmi
4 min readDec 15, 2023

--

Photo by Artem Sapegin on Unsplash

These two things are always asked in most of the interviews for JavaScript engineers. In one of my interviews even though I had extensive experience using them, yet I was unable to answer when the interviewer asked, What are Callback functions?

Their names are a lot more confusing than what they actually are in reality. In this article, I have tried my best to define them in technical terms and make it as simple as possible.

Callback functions

function someFunction(someParameter, callbackFunction) {
// after XYZ statements, there will be a condition where this function will
// the callbackFunction
callbackFunction(false, true) // Here, these two paramters that are passed to
// this function determine whether the above
// operation performed in this function was
// successful or false
}

//Now lets call that someFunction
someFunction("some URL here", function(success, error) {
if(error) {
// Do XYZ
} else {
// Move on...
}
}

If you look at the above function, this is basically what a callback function is. In simple words, this a function that is passed to another function and it helps to tell whether that function’s specific operation was successful or not. Now if you had used the NodeJS request library, you probably know it accepts the first argument as a URL and a callback function in the second argument. What it does internally is it invokes a request to the URL and upon receiving the response(either failed or successful) it CALLS BACK the provided function with error value in it’s argument true(if request was failed) or false(if it was able to successfully complete the HTTP request. This can also be some I/O operation using NodeJS FS library.

In more technical terms, Any function that is passed as an argument to another function and executes within that function after some XYZ operation is called a callback function.

Promises

const promiseBeingMade = new Promise((resolve, reject){
// Perform some async operation, I/O, Http requests, etc.
// Upon receiving results decide if it's valid or not
// resolve(someValue) if valid
// Otherwise reject(someError)
)

// Without using async/await
promiseBeingMade()
.then(res=> // Do something with the value passed to resolve(thisValue) function
.catch(err=> // Do something with the error passed to reject(thisError) function

// With async/await
async someFunction(){
try{
const res = await promiseBeingMade(); // It will contain the value passed
// to resolve(thisValue) if reject
// is not called otherwise, catch
// method below will be called and
// this try block will be ignored.
// Do something with the value in res
} catch(err //this is the error passed as an argument to reject function){
// Do something with the error
}

As you can see, Promises are more complex than callback function but they provide a lot cleaner syntax and you can avoid callback hell situation(when there are multiple nested callback functions). Basically, a Promise has an Object type and it accepts a function in its constructor during initialization. That function is called when the promise is being executed and it results in either success or failure.

There are two ways you can execute promises. One way is using it’s then and catch methods. The then method accepts a function in it’s parameter that executes the code you provide in that function after the promise is resolved successfully(resolve was called inside the definition body of promise). Catch method helps to catch any errors that come from the promise(reject was called inside the body of promise during execution).

Another method is using async/await syntax. For this syntax, you will have to declare a function with async keyword and then you can use the await keyword while executing the promise inside that function. Now you can use try/catch blocks and try is similar to the then method and catch is similar to the then method of promise except now you can call multiple promises using the await keyword that makes the code a lot morecleaner in case there are more than one promises that you want to execute one by one. If you will use then/catch methods, you will have to execute the second promise inside the then method and it will make the code difficult for reading and debugging.

Here is an example of executing multiple promises using both methods:

promiseOne.then((res)=> {
promiseTwo.then((resTwo)=> {
// anythihg you want to do with the response coming from promises
}).catch(err=> {
//In case of an error from promiseTwo
})
}).catch(err=>{
// In case of an error from promiseOne
})

// Using async/await

async function someFunction(){
try{
const responseOne = await promiseOne();
const responseTwo = await promiseTwo();
} catch(err) {
// Here this catch block catches thrown error from both promises(if any)
}
}

As you can see, the code using async/await keywords is a lot more cleaner.

So to conclude, A Promise in JavaScript has an Object type with then and catch methods for error handling and it can help while executing async operations by providing a lot more cleaner and robust code than callbacks.

Both of them (callbacks and promises) are helpful for executing async operations in JavaScript.

I hope now it was clearly understandable for you. If you want to add something, feel free to comment down below.

Thanks for reading!

--

--

Bilal Kazmi

I am a freelance Web Developer and enthusiastic coder, reader and writer.