Labs ICT
Pro Login

Promises and Async/Await Made Simple

JavaScript Concepts Explained 7 min read

Asynchronous JavaScript confuses many beginners. This article breaks down Promises and async/await with real examples.

Promises and Async/Await Made Simple

If you have ever seen code with arrows pointing in every direction, you have encountered callback hell. Promises and async/await are the modern solution to handling asynchronous operations cleanly.

What Does Asynchronous Mean?

Asynchronous code lets your program start a task, move on to other work, and come back when the task is done. Fetching data from an API, reading a file, or waiting for a timer are all asynchronous operations.

console.log("First")
setTimeout(() => console.log("Second"), 1000)
console.log("Third")

// Output: First, Third, Second

Callbacks and Callback Hell

A callback is a function you pass to another function to run later. When you nest callbacks too deeply, the code becomes unreadable. This problem is called callback hell.

getUser(id, (user) => {
  getOrders(user.id, (orders) => {
    getOrderDetails(orders[0].id, (details) => {
      console.log(details)
    })
  })
})

Promises: A Better Way

A Promise is an object that represents a value that will exist in the future. It can be in one of three states: pending, fulfilled, or rejected.

const myPromise = new Promise((resolve, reject) => {
  const success = true
  if (success) {
    resolve("It worked!")
  } else {
    reject("Something went wrong")
  }
})

myPromise
  .then((message) => console.log(message))
  .catch((error) => console.log(error))

Async/Await: Cleaner Syntax

Async/await is syntactic sugar over promises. An async function always returns a promise, and await pauses execution until the promise resolves.

async function fetchUser() {
  try {
    const response = await fetch("https://api.example.com/user")
    const data = await response.json()
    console.log(data)
  } catch (error) {
    console.log("Failed to fetch user:", error)
  }
}

Real Example: Fetching Data

Here is the same API call written with promises and async/await side by side.

// With promises
fetch("https://api.example.com/posts")
  .then((res) => res.json())
  .then((data) => console.log(data))
  .catch((err) => console.log(err))

// With async/await
async function getPosts() {
  try {
    const res = await fetch("https://api.example.com/posts")
    const data = await res.json()
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}

Error Handling

Promises use .catch() to handle errors. Async/await uses try/catch blocks. Both approaches let you gracefully handle failures without crashing your application.

Note: Use async/await for most new code. It is easier to read and debug. Callbacks are still fine for simple one-off operations like event listeners, and promises work well when you need to chain multiple async tasks.