Thursday, October 22, 2026 New tutorials published every day
Home
Tutorials
Core PHP PHP Course JavaScript React & Next.js Python WordPress Artificial Intelligence
Free Tools
JSON Formatter Base64 Encoder / Decoder Password Generator Text to QR Code Generator Word Counter Case Converter Age Calculator Color Converter Number to Words All Free Tools
Projects
PHP Projects — all projects
Resources
Search tutorials About CodingCush Contact
More
Login
JavaScript

JavaScript Promises and async/await Explained with Simple Examples

JavaScript Promises and async/await explained simply: callbacks, the pyramid problem,.then chains and clean await code — all on one small fetch example.

A JavaScript Promise is an object that represents a value you do not have yet, but will get later — like a token at a food counter that stands for the meal being prepared. Code that waits for data, timers or file reads uses Promises so the page never freezes while waiting. The async and await keywords are simply a cleaner way to write Promise code, letting asynchronous work read almost like normal, step-by-step code.

If callbacks ever left you staring at a staircase of nested functions wondering how you got there, this guide is for you. We will take one small example — loading a user from a server — and walk it through callbacks, then Promises, then async/await, so you can see exactly what each step improves.

The Problem: JavaScript Does Not Wait

JavaScript runs one line after another and does not pause at slow operations. Ask for data from a server and the next line runs immediately, long before the answer arrives. That is a feature, not a bug: while the network is busy, the page stays responsive and buttons still click. But it means you need a way to say "when the data finally arrives, then do this."

console.log('1. Asking the server for a user...');
setTimeout(function () {
    console.log('2. The user arrived (2 seconds later)');
}, 2000);
console.log('3. This line does NOT wait');

Run this and the order is 1, 3, 2. Line 3 refuses to wait, exactly as designed. Try it yourself in your browser console right now — it takes ten seconds and makes the idea stick far better than any diagram. So how do we run code in the right order when some steps are slow? For years, the answer was callbacks.

The Old Way: Callbacks, and the Pyramid Problem

A callback is a function you hand over, asking for it to be called when the work finishes. One callback is fine. The trouble starts when each step depends on the one before it: get the user, then get their orders, then get each order's delivery status. Each new step nests inside the previous one, and the code slides sideways into what developers cheerfully call the pyramid of doom.

getUser(7, function (user) {
    getOrders(user.id, function (orders) {
        getDelivery(orders[0].id, function (status) {
            console.log('Delivery status:', status);
        });
    });
});

It works, but reading it is hard, and handling an error means repeating checks inside every layer. Promises were added to JavaScript to flatten exactly this staircase.

What a Promise Is, in Plain Words

A Promise is in one of three states. It starts as pending, still waiting. It then settles in one of two ways: fulfilled, meaning the work succeeded and a value is ready, or rejected, meaning something went wrong and you get an error instead. A settled Promise never changes its mind, which makes the behaviour predictable. MDN's reference page for the Promise object describes these states and every method in full detail.

function waitTwoSeconds() {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve('Done waiting!');
        }, 2000);
    });
}

waitTwoSeconds().then(function (message) {
    console.log(message); // runs after 2 seconds
});

Inside the Promise, you call resolve(value) when things go well and reject(error) when they do not. Outside, .then() receives the success value, and .catch() receives any error. In practice you will use Promises returned by tools like fetch() far more often than you create your own.

Real screenshot of the MDN Web Docs page for the JavaScript Promise object
MDN's Promise reference — the states, methods and examples in one place.

Chaining: Promises Flatten the Pyramid

The real magic is that .then() returns a new Promise, so calls can chain in a straight line instead of nesting. Here is the same three-step job from earlier, rewritten flat:

fetch('https://jsonplaceholder.typicode.com/users/7')
    .then(function (response) { return response.json(); })
    .then(function (user) {
        console.log('User:', user.name);
        return fetch('https://jsonplaceholder.typicode.com/users/7/posts');
    })
    .then(function (response) { return response.json(); })
    .then(function (posts) {
        console.log('First post:', posts[0].title);
    })
    .catch(function (error) {
        console.log('Something went wrong:', error.message);
    });

Read it top to bottom, like a recipe. One .catch() at the end catches an error from any step above it — no repeating the same check at every level. That single change is why Promises replaced nested callbacks across the whole ecosystem.

async/await: The Same Promises, Written Simply

JavaScript then added async and await, which are not a new system at all — they are new spelling for the same Promises. Mark a function with async and it automatically returns a Promise. Inside it, await pauses that function (not the whole page) until a Promise settles, and hands you the value directly. MDN's page on the async function declaration shows how the two keywords fit together.

async function showFirstPost() {
    try {
        const response = await fetch('https://jsonplaceholder.typicode.com/users/7');
        const user = await response.json();
        console.log('User:', user.name);

        const postsResponse = await fetch('https://jsonplaceholder.typicode.com/users/7/posts');
        const posts = await postsResponse.json();
        console.log('First post:', posts[0].title);
    } catch (error) {
        console.log('Something went wrong:', error.message);
    }
}

showFirstPost();

Compare the two versions honestly: identical work, identical Promises underneath, but the async/await version reads like ordinary code and uses the familiar try/catch for errors. This is why almost all modern JavaScript you will meet at work is written this way.

Style How success is handled How errors are handled Best for
Callbacks Pass a function to run later Check inside every layer Very old code and simple timers
Promises with .then() Chain .then() calls in a line One .catch() at the end Chained steps and parallel work
async/await await each step, top to bottom try/catch around the awaits Most new code you write

One Useful Extra: Running Promises Together

Sometimes steps do not depend on each other — you need a user's profile and their settings, but either can arrive first. Awaiting them one by one wastes time. Promise.all() starts them together and gives you all the results when the slowest one finishes, which can make a page feel much snappier.

const [userRes, postsRes] = await Promise.all([
    fetch('https://jsonplaceholder.typicode.com/users/7'),
    fetch('https://jsonplaceholder.typicode.com/users/7/posts')
]);

If any one of them fails, Promise.all() rejects as a whole, so keep the try/catch around it. Use it when the steps are truly independent, and plain sequential awaits when each step needs the previous result.

Where This Fits With What You Already Know

Promises sit underneath almost everything in modern JavaScript: fetching API data, loading images, timers, and most library calls. If array work is your next topic, our guide to JavaScript array methods pairs nicely, because real code usually fetches an array and then transforms it. And if variable declarations still trip you up while reading examples, our var vs let vs const explainer clears that up in minutes. More guides are gathered in the Learn JavaScript hub.

Frequently Asked Questions

Is async/await different from Promises?

No. async/await is syntax built on top of Promises. An async function returns a Promise, and await pauses until a Promise settles. You can mix both styles in the same project without any problem.

Does await block the whole browser page?

No. await pauses only the async function it sits in. The rest of the page — clicks, scrolling, other scripts — keeps running normally while the Promise is pending.

What happens if I forget await?

You get the Promise object itself instead of its value, so printing it shows "Promise { pending }" instead of your data. It is one of the most common beginner bugs, and now you will recognise it instantly.

Should I learn .then() if async/await exists?

Yes. You will meet .then() in older code, library examples and interviews, and Promise methods like Promise.all() are used inside async functions too. Learn to read both; write mostly async/await.

Conclusion

Callbacks asked you to nest. Promises let you chain in a straight line with one place for errors. async/await then made the same ideas read like plain, top-to-bottom code. Learn the food-token picture — pending, fulfilled, rejected — practise the fetch example twice, and asynchronous JavaScript stops being the scary chapter and becomes the chapter everything else stands on.

Lavkush
Written by

Lavkush Super Admin

Lavkush Kumar is a software developer with over 6 years of experience turning ideas into websites and web applications. He holds a Master of Computer Applications (MCA) degree and enjoys building for the web, exploring new tools, and sharing what he learns along the way.

Through CodingCush, Lavkush creates easy-to-follow tutorials and practical guides to help students and beginners learn software development without feeling overwhelmed. His approach is simple: learn by doing, build real projects, and keep improving one step at a time.

Whether you’re just starting out or looking to sharpen your skills, CodingCush is all about making learning practical, beginner-friendly, and fun.

0 Comments

Leave a Comment